| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
- # For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
- # Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
- """Optional checker to warn when loop variables are overwritten in the loop's body."""
- from __future__ import annotations
- from astroid import nodes
- from pylint import checkers
- from pylint.checkers import utils
- from pylint.interfaces import HIGH
- from pylint.lint import PyLinter
- class RedefinedLoopNameChecker(checkers.BaseChecker):
- 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: PyLinter) -> None:
- super().__init__(linter)
- self._loop_variables: list[
- tuple[nodes.For, list[str], nodes.LocalsDictNodeNG]
- ] = []
- @utils.only_required_for_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, outer_for_scope in self._loop_variables:
- if node_scope is not outer_for_scope:
- continue
- if node.name in outer_variables and not utils.in_for_else_branch(
- outer_for, node
- ):
- self.add_message(
- "redefined-loop-name",
- args=(node.name, outer_for.fromlineno),
- node=node,
- confidence=HIGH,
- )
- break
- @utils.only_required_for_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
- assigned_to = [
- var
- for var in assigned_to
- if not self.linter.config.dummy_variables_rgx.match(var)
- ]
- node_scope = node.scope()
- for variable in assigned_to:
- for outer_for, outer_variables, outer_for_scope in self._loop_variables:
- if node_scope is not outer_for_scope:
- continue
- if variable in outer_variables and not utils.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, node.scope()))
- @utils.only_required_for_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(RedefinedLoopNameChecker(linter))
|