Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,31 @@ def inner():
g(x, idx)
idx += 1


def func():
# SIM113 x2 (same variable name reused in sibling loops)
i = 0
for val in [1, 2, 3]:
print(f"{i}: {val}")
i += 1

i = 0
for val in [1, 2, 3]:
print(f"{i}: {val}")
i += 1


def func():
# SIM113 (same variable name reused after an `enumerate` loop)
for i, val in enumerate([1, 2, 3]):
print(f"{i}: {val}")

i = 0
for val in [1, 2, 3]:
print(f"{i}: {val}")
i += 1


async def func():
# OK (for loop is async)
idx = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,19 @@ pub(crate) fn enumerate_for_loop(checker: &Checker, for_stmt: &ast::StmtFor) {
};

// If it's not an assignment (e.g., it's a function argument), ignore it.
let binding = checker.semantic().binding(id);
if !binding.kind.is_assignment() {
let initial_binding = checker.semantic().binding(id);
if !initial_binding.kind.is_assignment() {
continue;
}

// If the variable is global or nonlocal, ignore it.
if binding.is_global() || binding.is_nonlocal() {
if initial_binding.is_global() || initial_binding.is_nonlocal() {
continue;
}

// Ensure that the index variable was initialized to 0 (or instance of `int` if preview is enabled).
let Some(value) = typing::find_binding_value(binding, checker.semantic()) else {
let Some(value) = typing::find_binding_value(initial_binding, checker.semantic())
else {
continue;
};
if !(matches!(
Expand All @@ -112,7 +113,7 @@ pub(crate) fn enumerate_for_loop(checker: &Checker, for_stmt: &ast::StmtFor) {
let Some(for_loop_id) = checker.semantic().current_statement_id() else {
continue;
};
let Some(assignment_id) = binding.source else {
let Some(assignment_id) = initial_binding.source else {
continue;
};
if checker.semantic().parent_statement_id(for_loop_id)
Expand All @@ -124,7 +125,7 @@ pub(crate) fn enumerate_for_loop(checker: &Checker, for_stmt: &ast::StmtFor) {
// Identify the binding created by the augmented assignment.
// TODO(charlie): There should be a way to go from `ExprName` to `BindingId` (like
// `resolve_name`, but for bindings rather than references).
let binding = {
let increment_binding = {
let mut bindings = checker
.semantic()
.current_scope()
Expand All @@ -144,10 +145,13 @@ pub(crate) fn enumerate_for_loop(checker: &Checker, for_stmt: &ast::StmtFor) {
binding
};

// If the variable is used outside the loop, ignore it.
if binding.references.iter().any(|id| {
let reference = checker.semantic().reference(*id);
!for_stmt.range().contains_range(reference.range())
// Reassignments in the same scope inherit older references. Ignore anything that
// predates the counter's initialization and only consider uses in its current lifetime.
let initial_binding_start = initial_binding.range.start();
if increment_binding.references().any(|id| {
let reference = checker.semantic().reference(id);
reference.start() >= initial_binding_start
&& !for_stmt.range().contains_range(reference.range())
}) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,32 @@ SIM113 Use `enumerate()` for index variable `idx` in `for` loop
| ^^^^^^^^
45 | h(x)
|

SIM113 Use `enumerate()` for index variable `i` in `for` loop
--> SIM113.py:204:9
|
202 | for val in [1, 2, 3]:
203 | print(f"{i}: {val}")
204 | i += 1
| ^^^^^^
205 |
206 | i = 0
|

SIM113 Use `enumerate()` for index variable `i` in `for` loop
--> SIM113.py:209:9
|
207 | for val in [1, 2, 3]:
208 | print(f"{i}: {val}")
209 | i += 1
| ^^^^^^
|

SIM113 Use `enumerate()` for index variable `i` in `for` loop
--> SIM113.py:220:9
|
218 | for val in [1, 2, 3]:
219 | print(f"{i}: {val}")
220 | i += 1
| ^^^^^^
|
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,32 @@ SIM113 Use `enumerate()` for index variable `idx` in `for` loop
| ^^^^^^^^
55 | h(x)
|

SIM113 Use `enumerate()` for index variable `i` in `for` loop
--> SIM113.py:204:9
|
202 | for val in [1, 2, 3]:
203 | print(f"{i}: {val}")
204 | i += 1
| ^^^^^^
205 |
206 | i = 0
|

SIM113 Use `enumerate()` for index variable `i` in `for` loop
--> SIM113.py:209:9
|
207 | for val in [1, 2, 3]:
208 | print(f"{i}: {val}")
209 | i += 1
| ^^^^^^
|

SIM113 Use `enumerate()` for index variable `i` in `for` loop
--> SIM113.py:220:9
|
218 | for val in [1, 2, 3]:
219 | print(f"{i}: {val}")
220 | i += 1
| ^^^^^^
|
Loading