-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
[optional ext] Emit redefined-loop-name
for redefinitions of loop variables in body
#5649
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
jacobtylerwalls
merged 38 commits into
pylint-dev:main
from
jacobtylerwalls:redefined-loop-var
May 2, 2022
Merged
Changes from 23 commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
a80ad9f
Fix #5608: Emit `redefined-outer-name` for redefinitions of loop vari…
jacobtylerwalls fa33c2e
Extend solution to AugAssign
jacobtylerwalls 859b01e
Merge branch 'main' into redefined-loop-var
jacobtylerwalls 5929ce7
Update tests
jacobtylerwalls 91e118a
Fix false positive involving functions nested under loops
jacobtylerwalls 5b61d0a
Apply suggestions from code review
jacobtylerwalls c80336d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 506c194
Update test results
jacobtylerwalls a67f984
Remove is_message_enabled() call
jacobtylerwalls e6791e2
Create `redefined-loop-name` message
jacobtylerwalls 05dc2df
Update redefined-outer-name description
jacobtylerwalls 45987bb
Merge branch 'main' into redefined-loop-var
jacobtylerwalls 75f98a9
Make `redefined-loop-name` an optional extension
jacobtylerwalls 7147199
Delete relocated code
jacobtylerwalls c6e9766
Remove moved message description
jacobtylerwalls 46e0f59
Remove disables
jacobtylerwalls 8378199
Bump message ID
jacobtylerwalls 19962f6
Add examples
jacobtylerwalls c2b5d4e
Merge branch 'main' into redefined-loop-var
jacobtylerwalls b5c6aa5
Add pylintrc
jacobtylerwalls 09705bd
Add coverage and fix preexisting false positive
jacobtylerwalls 5970936
Add coverage
jacobtylerwalls cb3cfbf
Merge branch 'main' into redefined-loop-var
jacobtylerwalls f15d662
Apply suggestions from code review
jacobtylerwalls 3cc7f0a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 3fb8477
Use self.linter.config and remove test file artifact
jacobtylerwalls 86a272e
Merge branch 'redefined-loop-var' of https://github.com/jacobtylerwal…
jacobtylerwalls e6e4834
Move to checker utils
jacobtylerwalls 0c23bdf
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] d2b487c
typo
jacobtylerwalls 0f67bc9
Fix typing
jacobtylerwalls 2824a1e
Better names
jacobtylerwalls d506334
Add caching
jacobtylerwalls 7c13e3a
keep scope() around
jacobtylerwalls efc734f
more specific type
jacobtylerwalls e21a932
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 3990ee6
Remove now unused list
jacobtylerwalls 836ef8a
Fix existing typo
jacobtylerwalls File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
for item in names: | ||
item = item.lower() # [redefined-loop-name] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
for item in names: | ||
lowercased = item.lower() | ||
jacobtylerwalls marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[MASTER] | ||
load-plugins=pylint.extensions.redefined_loop_name, |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html | ||
# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE | ||
# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt | ||
|
||
"""Optional checker to warn when loop variables are overwritten in the loop's body.""" | ||
|
||
from astroid import nodes | ||
|
||
from pylint import checkers, interfaces | ||
from pylint.checkers import utils | ||
from pylint.checkers.variables import in_for_else_branch | ||
jacobtylerwalls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
from pylint.interfaces import HIGH | ||
from pylint.lint import PyLinter | ||
from pylint.utils import utils as pylint_utils | ||
|
||
|
||
class RedefinedLoopName(checkers.BaseChecker): | ||
|
||
__implements__ = interfaces.IAstroidChecker | ||
jacobtylerwalls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
name = "redefined-loop-name" | ||
|
||
msgs = { | ||
"W2901": ( | ||
"Redefining %r from loop (line %s)", | ||
"redefined-loop-name", | ||
"Used when a loop variable is overwritten in the loop body.", | ||
), | ||
} | ||
|
||
def __init__(self, linter=None): | ||
jacobtylerwalls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
super().__init__(linter) | ||
self._loop_variables = [] | ||
jacobtylerwalls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
@utils.check_messages("redefined-loop-name") | ||
def visit_assignname(self, node: nodes.AssignName) -> None: | ||
assign_type = node.assign_type() | ||
if not isinstance(assign_type, (nodes.Assign, nodes.AugAssign)): | ||
return | ||
node_scope = node.scope() | ||
for outer_for, outer_variables in self._loop_variables: | ||
if node_scope is not outer_for.scope(): | ||
jacobtylerwalls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
continue | ||
if node.name in outer_variables and not in_for_else_branch(outer_for, node): | ||
self.add_message( | ||
"redefined-loop-name", | ||
args=(node.name, outer_for.fromlineno), | ||
DanielNoord marked this conversation as resolved.
Show resolved
Hide resolved
|
||
node=node, | ||
confidence=HIGH, | ||
) | ||
break | ||
|
||
@utils.check_messages("redefined-loop-name") | ||
def visit_for(self, node: nodes.For) -> None: | ||
assigned_to = [a.name for a in node.target.nodes_of_class(nodes.AssignName)] | ||
# Only check variables that are used | ||
dummy_rgx = pylint_utils.get_global_option( | ||
jacobtylerwalls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self, "dummy-variables-rgx", default=None | ||
) | ||
assigned_to = [var for var in assigned_to if not dummy_rgx.match(var)] | ||
|
||
node_scope = node.scope() | ||
for variable in assigned_to: | ||
for outer_for, outer_variables in self._loop_variables: | ||
if node_scope is not outer_for.scope(): | ||
continue | ||
if variable in outer_variables and not in_for_else_branch( | ||
outer_for, node | ||
): | ||
self.add_message( | ||
"redefined-loop-name", | ||
args=(variable, outer_for.fromlineno), | ||
node=node, | ||
confidence=HIGH, | ||
) | ||
break | ||
|
||
self._loop_variables.append((node, assigned_to)) | ||
|
||
@utils.check_messages("redefined-loop-name") | ||
def leave_for(self, node: nodes.For) -> None: # pylint: disable=unused-argument | ||
self._loop_variables.pop() | ||
|
||
|
||
def register(linter: PyLinter) -> None: | ||
linter.register_checker(RedefinedLoopName(linter)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
tests/functional/ext/redefined_loop_name/redefined_loop_name.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
"""Tests for redefinitions of loop variables inside the loop body. | ||
jacobtylerwalls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
See: https://github.com/PyCQA/pylint/issues/5608 | ||
""" | ||
# pylint: disable=invalid-name | ||
|
||
lines = ["1\t", "2\t"] | ||
for line in lines: | ||
line = line.strip() # [redefined-loop-name] | ||
|
||
for i in range(8): | ||
for j in range(8): | ||
for i in range(j): # [redefined-loop-name] | ||
j = i # [redefined-loop-name] | ||
print(i, j) | ||
|
||
for i in range(8): | ||
for j in range(8): | ||
for k in range(j): | ||
k = (i, j) # [redefined-loop-name] | ||
print(i, j, k) | ||
|
||
lines = [(1, "1\t"), (2, "2\t")] | ||
for i, line in lines: | ||
line = line.strip() # [redefined-loop-name] | ||
|
||
line = "no warning" | ||
|
||
for i in range(10): | ||
i += 1 # [redefined-loop-name] | ||
|
||
|
||
def outer(): | ||
"""No redefined-loop-name for variables in nested scopes""" | ||
for i1 in range(5): | ||
def inner(): | ||
"""No warning, because i has a new scope""" | ||
for i1 in range(3): | ||
print(i1) | ||
print(i1) | ||
inner() | ||
|
||
|
||
def outer2(): | ||
"""Similar, but with an assignment instead of homonymous loop variables""" | ||
for i1 in range(5): | ||
def inner(): | ||
"""No warning, because i has a new scope""" | ||
for _ in range(3): | ||
i1 = 0 | ||
print(i1) | ||
print(i1) | ||
inner() |
2 changes: 2 additions & 0 deletions
2
tests/functional/ext/redefined_loop_name/redefined_loop_name.rc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[MASTER] | ||
load-plugins=pylint.extensions.redefined_loop_name, |
6 changes: 6 additions & 0 deletions
6
tests/functional/ext/redefined_loop_name/redefined_loop_name.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
redefined-loop-name:9:4:9:8::Redefining 'line' from loop (line 8):HIGH | ||
redefined-loop-name:13:8:15:23::Redefining 'i' from loop (line 11):HIGH | ||
redefined-loop-name:14:12:14:13::Redefining 'j' from loop (line 12):HIGH | ||
redefined-loop-name:20:12:20:13::Redefining 'k' from loop (line 19):HIGH | ||
redefined-loop-name:25:4:25:8::Redefining 'line' from loop (line 24):HIGH | ||
redefined-loop-name:30:4:30:5::Redefining 'i' from loop (line 29):HIGH |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
tests/functional/ext/redefined_loop_name/reused_outer_loop_variable.rc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[MASTER] | ||
load-plugins=pylint.extensions.redefined_loop_name, | ||
DanielNoord marked this conversation as resolved.
Show resolved
Hide resolved
|
5 changes: 5 additions & 0 deletions
5
tests/functional/ext/redefined_loop_name/reused_outer_loop_variable.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
redefined-loop-name:7:4:8:16::Redefining 'i' from loop (line 6):HIGH | ||
redefined-loop-name:12:4:13:25::Redefining 'i' from loop (line 11):HIGH | ||
redefined-loop-name:17:4:18:25::Redefining 'i' from loop (line 16):HIGH | ||
redefined-loop-name:22:4:23:25::Redefining 'a' from loop (line 21):HIGH | ||
redefined-loop-name:41:4:42:16::Redefining 'j' from loop (line 40):HIGH |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
redefined-loop-name:9:4:9:8::Redefining 'line' from loop (line 8):HIGH | ||
jacobtylerwalls marked this conversation as resolved.
Show resolved
Hide resolved
|
||
redefined-loop-name:13:8:15:23::Redefining 'i' from loop (line 11):UNDEFINED | ||
redefined-loop-name:14:12:14:13::Redefining 'j' from loop (line 12):HIGH | ||
redefined-loop-name:20:12:20:13::Redefining 'k' from loop (line 19):HIGH | ||
redefined-loop-name:25:4:25:8::Redefining 'line' from loop (line 24):HIGH | ||
redefined-loop-name:30:4:30:5::Redefining 'i' from loop (line 29):HIGH |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.