Skip to content

Commit 2de3d11

Browse files
Fix pylint-dev#5608: Emit redefined-outer-name for redefinitions of loop variables in body
1 parent 3fc855f commit 2de3d11

10 files changed

+54
-7
lines changed

.copyrite_aliases

+5
Original file line numberDiff line numberDiff line change
@@ -113,5 +113,10 @@
113113
114114
"authoritative_mail": "[email protected]",
115115
"name": "Tushar Sadhwani"
116+
},
117+
{
118+
"mails": ["[email protected]"],
119+
"authoritative_mail": "[email protected]",
120+
"name": "Jacob Walls"
116121
}
117122
]

ChangeLog

+5
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ Release date: TBA
124124
Closes #4434
125125
Closes #5370
126126

127+
* Emit ``redefined-outer-name`` when a loop variable is redefined in the loop
128+
body. Previously, only redefinitions taking place in nested loops were flagged.
129+
130+
Closes #5608
131+
127132
* ``encoding`` can now be supplied as a positional argument to calls that open
128133
files without triggering ``unspecified-encoding``.
129134

doc/whatsnew/2.13.rst

+5
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,11 @@ Other Changes
127127
Closes #4434
128128
Closes #5370
129129

130+
* Emit ``redefined-outer-name`` when a loop variable is redefined in the loop
131+
body. Previously, only redefinitions taking place in nested loops were flagged.
132+
133+
Closes #5608
134+
130135
* ``encoding`` can now be supplied as a positional argument to calls that open
131136
files without triggering ``unspecified-encoding``.
132137

pylint/checkers/variables.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -482,9 +482,10 @@ def _has_locals_call_after_node(stmt, scope):
482482
"`'from X import *'` style import.",
483483
),
484484
"W0621": (
485-
"Redefining name %r from outer scope (line %s)",
485+
"Redefining name %r from outer scope or loop (line %s)",
486486
"redefined-outer-name",
487-
"Used when a variable's name hides a name defined in the outer scope.",
487+
"Used when a variable's name hides a name defined in an outer scope, "
488+
"for loop, or except handler.",
488489
),
489490
"W0622": (
490491
"Redefining built-in %r",
@@ -740,7 +741,7 @@ class VariablesChecker(BaseChecker):
740741
"""checks for
741742
* unused variables / imports
742743
* undefined variables
743-
* redefinition of variable from builtins or from an outer scope
744+
* redefinition of variable from builtins or from an outer scope, for loop, or except handler
744745
* use of variable before assignment
745746
* __all__ consistency
746747
* self/cls assignment
@@ -1112,6 +1113,19 @@ def visit_global(self, node: nodes.Global) -> None:
11121113
self.add_message("global-statement", node=node)
11131114

11141115
def visit_assignname(self, node: nodes.AssignName) -> None:
1116+
if self.linter.is_message_enabled("redefined-outer-name") and isinstance(
1117+
node.parent, nodes.Assign
1118+
):
1119+
for outer_for, outer_variables in self._loop_variables:
1120+
if node.name in outer_variables and not in_for_else_branch(
1121+
outer_for, node
1122+
):
1123+
self.add_message(
1124+
"redefined-outer-name",
1125+
args=(node.name, outer_for.fromlineno),
1126+
node=node,
1127+
)
1128+
break
11151129
if isinstance(node.assign_type(), nodes.AugAssign):
11161130
self.visit_name(node)
11171131

tests/functional/b/bad_indentation.py

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def titii():
1313

1414
def tataa(kdict):
1515
for key in ['1', '2', '3']:
16+
# pylint: disable=redefined-outer-name
1617
key = key.lower()
1718

1819
if key in kdict:

tests/functional/c/cellvar_escaping_loop.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def good_case10():
7676
lst = []
7777
for i in range(10): # pylint: disable=unused-variable
7878
def func():
79-
i = 100
79+
i = 100 # pylint: disable=redefined-outer-name
8080
def func2(arg=i):
8181
return arg
8282

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""Tests for redefinitions of loop variables inside the loop body.
2+
3+
See: https://github.com/PyCQA/pylint/issues/5608
4+
"""
5+
# pylint: disable=invalid-name
6+
7+
lines = ["1\t", "2\t"]
8+
for line in lines:
9+
line = line.strip() # [redefined-outer-name]
10+
11+
lines = [(1, "1\t"), (2, "2\t")]
12+
for i, line in lines:
13+
line = line.strip() # [redefined-outer-name]
14+
15+
line = "no warning"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
redefined-outer-name:9:4:9:8::Redefining name 'line' from outer scope (line 8):UNDEFINED
2+
redefined-outer-name:13:4:13:8::Redefining name 'line' from outer scope (line 12):UNDEFINED

tests/functional/u/undefined/undefined_loop_variable.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def do_else(some_random_list):
2121
VAR2 = B # nor this one
2222

2323
for var1, var2 in TEST_LC:
24-
var1 = var2 + 4
24+
var1 = var2 + 4 # pylint: disable=redefined-outer-name
2525
VAR3 = var1 # [undefined-loop-variable]
2626

2727
for note in __revision__:

tests/functional/u/unnecessary/unnecessary_dict_index_lookup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
for k, v in b_dict.items():
1414
print(k)
15-
k = "another key"
15+
k = "another key" # pylint: disable=redefined-outer-name
1616
print(b_dict[k]) # This is fine, key reassigned
1717

1818

@@ -68,7 +68,7 @@ class Foo:
6868
for item in d.items():
6969
print(item[0])
7070
print(d[item[0]]) # [unnecessary-dict-index-lookup]
71-
item = (2, "b")
71+
item = (2, "b") # pylint: disable=redefined-outer-name
7272
print(d[item[0]]) # This is fine, no warning thrown as key has been reassigned
7373

7474

0 commit comments

Comments
 (0)