diff options
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | doc/whatsnew/2.13.rst | 7 | ||||
-rw-r--r-- | pylint/checkers/variables.py | 19 | ||||
-rw-r--r-- | tests/functional/u/unused/unused_argument_py3.py | 9 | ||||
-rw-r--r-- | tests/functional/u/unused/unused_name_from_wildcard_import.py (renamed from tests/functional/u/unused/unused_name_from_wilcard_import.py) | 0 | ||||
-rw-r--r-- | tests/functional/u/unused/unused_name_from_wildcard_import.txt (renamed from tests/functional/u/unused/unused_name_from_wilcard_import.txt) | 2 |
6 files changed, 38 insertions, 6 deletions
@@ -16,6 +16,7 @@ Release date: TBA Closes #578 + * Fix false negative for ``no-member`` when attempting to assign an instance attribute to itself without any prior assignment. @@ -41,6 +42,12 @@ Release date: TBA Closes #2793 +* Fixed false positive for ``unused-argument`` when a ``nonlocal`` name is used + in a nested function that is returned without being called by its parent. + + Closes #5187 + + What's New in Pylint 2.13.0? ============================ Release date: 2022-03-24 diff --git a/doc/whatsnew/2.13.rst b/doc/whatsnew/2.13.rst index 856553983..b0bfd1321 100644 --- a/doc/whatsnew/2.13.rst +++ b/doc/whatsnew/2.13.rst @@ -548,7 +548,12 @@ Other Changes Closes #5950 +* Fixed false positive for ``unused-argument`` when a ``nonlocal`` name is used + in a nested function that is returned without being called by its parent. + + Closes #5187 + * Avoid emitting ``raising-bad-type`` when there is inference ambiguity on the variable being raised. - Closes #2793
\ No newline at end of file + Closes #2793 diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 08acaefb8..15fa5008b 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -16,6 +16,8 @@ from typing import ( Any, DefaultDict, Dict, + Iterable, + Iterator, List, NamedTuple, Optional, @@ -343,7 +345,9 @@ def _import_name_is_global(stmt, global_names): return False -def _flattened_scope_names(iterator): +def _flattened_scope_names( + iterator: Iterator[Union[nodes.Global, nodes.Nonlocal]] +) -> Set[str]: values = (set(stmt.names) for stmt in iterator) return set(itertools.chain.from_iterable(values)) @@ -2271,7 +2275,9 @@ class VariablesChecker(BaseChecker): if not elements: self.add_message("undefined-loop-variable", args=node.name, node=node) - def _check_is_unused(self, name, node, stmt, global_names, nonlocal_names): + def _check_is_unused( + self, name, node, stmt, global_names, nonlocal_names: Iterable[str] + ): # Ignore some special names specified by user configuration. if self._is_name_ignored(stmt, name): return @@ -2293,7 +2299,7 @@ class VariablesChecker(BaseChecker): argnames = node.argnames() # Care about functions with unknown argument (builtins) if name in argnames: - self._check_unused_arguments(name, node, stmt, argnames) + self._check_unused_arguments(name, node, stmt, argnames, nonlocal_names) else: if stmt.parent and isinstance( stmt.parent, (nodes.Assign, nodes.AnnAssign, nodes.Tuple) @@ -2360,7 +2366,9 @@ class VariablesChecker(BaseChecker): regex = authorized_rgx return regex and regex.match(name) - def _check_unused_arguments(self, name, node, stmt, argnames): + def _check_unused_arguments( + self, name, node, stmt, argnames, nonlocal_names: Iterable[str] + ): is_method = node.is_method() klass = node.parent.frame(future=True) if is_method and isinstance(klass, nodes.ClassDef): @@ -2401,6 +2409,9 @@ class VariablesChecker(BaseChecker): if utils.is_protocol_class(klass): return + if name in nonlocal_names: + return + self.add_message("unused-argument", args=name, node=stmt, confidence=confidence) def _check_late_binding_closure(self, node: nodes.Name) -> None: diff --git a/tests/functional/u/unused/unused_argument_py3.py b/tests/functional/u/unused/unused_argument_py3.py index 4d0fd9adc..2529e6caa 100644 --- a/tests/functional/u/unused/unused_argument_py3.py +++ b/tests/functional/u/unused/unused_argument_py3.py @@ -7,3 +7,12 @@ def func(first, *, second): # [unused-argument, unused-argument] def only_raises(first, second=42): # [unused-argument] if first == 24: raise ValueError + + +def increment_factory(initial): + + def increment(): + nonlocal initial + initial += 1 + + return increment diff --git a/tests/functional/u/unused/unused_name_from_wilcard_import.py b/tests/functional/u/unused/unused_name_from_wildcard_import.py index 54658b855..54658b855 100644 --- a/tests/functional/u/unused/unused_name_from_wilcard_import.py +++ b/tests/functional/u/unused/unused_name_from_wildcard_import.py diff --git a/tests/functional/u/unused/unused_name_from_wilcard_import.txt b/tests/functional/u/unused/unused_name_from_wildcard_import.txt index c939b8260..0289a13fb 100644 --- a/tests/functional/u/unused/unused_name_from_wilcard_import.txt +++ b/tests/functional/u/unused/unused_name_from_wildcard_import.txt @@ -1,4 +1,4 @@ -unused-wildcard-import:3:0:3:34::Unused import(s) func and only_raises from wildcard import of unused_argument_py3:UNDEFINED +unused-wildcard-import:3:0:3:34::Unused import(s) func, only_raises and increment_factory from wildcard import of unused_argument_py3:UNDEFINED wildcard-import:3:0:3:34::Wildcard import unused_argument_py3:UNDEFINED unused-wildcard-import:4:0:4:38::Unused import(s) VAR from wildcard import of unused_global_variable1:UNDEFINED wildcard-import:4:0:4:38::Wildcard import unused_global_variable1:UNDEFINED |