diff --git a/doc/user_guide/checkers/features.rst b/doc/user_guide/checkers/features.rst index a23c57c3eb..5780968828 100644 --- a/doc/user_guide/checkers/features.rst +++ b/doc/user_guide/checkers/features.rst @@ -65,6 +65,8 @@ Basic checker Messages Emitted when a name is used prior a global declaration, which results in an error since Python 3.6. This message can't be emitted when using Python < 3.6. +:name-is-parameter-and-nonlocal (E0134): *Name %r is both parameter and nonlocal* + Emitted when a nonlocal variable is also a parameter. :return-outside-function (E0104): *Return outside function* Used when a "return" statement is found outside a function or method. :return-arg-in-generator (E0106): *Return with argument inside generator* diff --git a/doc/user_guide/messages/messages_overview.rst b/doc/user_guide/messages/messages_overview.rst index 558b3f1bf9..935de02457 100644 --- a/doc/user_guide/messages/messages_overview.rst +++ b/doc/user_guide/messages/messages_overview.rst @@ -120,6 +120,7 @@ All messages in the error category: error/mixed-format-string error/modified-iterating-dict error/modified-iterating-set + error/name-is-parameter-and-nolocal error/no-member error/no-method-argument error/no-name-in-module diff --git a/doc/whatsnew/fragments/6882.new_check b/doc/whatsnew/fragments/6882.new_check new file mode 100644 index 0000000000..019eb44efe --- /dev/null +++ b/doc/whatsnew/fragments/6882.new_check @@ -0,0 +1,3 @@ +Added ``name-is-parameter-and-nonlocal`` which flags when a nonlocal variable is also a paremeter. + +Closes #6882 diff --git a/pylint/checkers/base/basic_error_checker.py b/pylint/checkers/base/basic_error_checker.py index f76c5aa8b6..e9c36322b4 100644 --- a/pylint/checkers/base/basic_error_checker.py +++ b/pylint/checkers/base/basic_error_checker.py @@ -204,6 +204,11 @@ class BasicErrorChecker(_BasicChecker): "which results in an error since Python 3.6.", {"minversion": (3, 6)}, ), + "E0134": ( + "Name %r is both parameter and nonlocal", + "name-is-parameter-and-nonlocal", + "Emitted when a nonlocal variable is also a parameter.", + ), } def open(self) -> None: @@ -264,9 +269,11 @@ def visit_starred(self, node: nodes.Starred) -> None: "duplicate-argument-name", "nonlocal-and-global", "used-prior-global-declaration", + "name-is-parameter-and-nonlocal", ) def visit_functiondef(self, node: nodes.FunctionDef) -> None: self._check_nonlocal_and_global(node) + self._check_nonlocal_and_parameter(node) self._check_name_used_prior_global(node) if not redefined_by_decorator( node @@ -327,6 +334,19 @@ def _check_name_used_prior_global(self, node: nodes.FunctionDef) -> None: "used-prior-global-declaration", node=node_name, args=(name,) ) + def _check_nonlocal_and_parameter(self, node: nodes.FunctionDef) -> None: + """Check if a name is both parameter and nonlocal.""" + node_arg_names = {arg.name for arg in node.args.args} + for body_node in node.nodes_of_class(nodes.Nonlocal): + for non_local_name in body_node.names: + if non_local_name in node_arg_names: + self.add_message( + "name-is-parameter-and-nonlocal", + args=(non_local_name,), + node=body_node, + confidence=HIGH, + ) + def _check_nonlocal_and_global(self, node: nodes.FunctionDef) -> None: """Check that a name is both nonlocal and global.""" diff --git a/tests/functional/n/name/name_is_parameter_and_nonlocal.py b/tests/functional/n/name/name_is_parameter_and_nonlocal.py new file mode 100644 index 0000000000..10406a1c4b --- /dev/null +++ b/tests/functional/n/name/name_is_parameter_and_nonlocal.py @@ -0,0 +1,6 @@ +# pylint: disable=missing-docstring,invalid-name,nonlocal-without-binding + + +def tomato(is_tasty: bool = True): + nonlocal color + nonlocal is_tasty # [name-is-parameter-and-nonlocal] diff --git a/tests/functional/n/name/name_is_parameter_and_nonlocal.txt b/tests/functional/n/name/name_is_parameter_and_nonlocal.txt new file mode 100644 index 0000000000..c8322e45f4 --- /dev/null +++ b/tests/functional/n/name/name_is_parameter_and_nonlocal.txt @@ -0,0 +1 @@ +name-is-parameter-and-nonlocal:6:4:6:21:tomato:Name 'is_tasty' is both parameter and nonlocal:HIGH