diff options
author | Bryce Guinta <bryce.paul.guinta@gmail.com> | 2018-02-26 17:19:51 -0700 |
---|---|---|
committer | Bryce Guinta <bryce.paul.guinta@gmail.com> | 2018-03-03 01:10:02 -0700 |
commit | 2746fe3a0faaf7bc5cd002af9408460f383d867b (patch) | |
tree | a07d111d35a9f2820400ce4e6bc007732c805231 | |
parent | fbb93327a3e85a3c4661261c0bd6a0735a53acf7 (diff) | |
download | pylint-git-2746fe3a0faaf7bc5cd002af9408460f383d867b.tar.gz |
Check if node is in list comprehension in function defaults
List comprehensions in python 2 do not have scope. If the local scope is
skipped, then generated variables values will raise a false-positive.
Close #1897
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | doc/whatsnew/1.8.rst | 3 | ||||
-rw-r--r-- | pylint/checkers/variables.py | 8 | ||||
-rw-r--r-- | pylint/test/unittest_checker_variables.py | 22 |
4 files changed, 36 insertions, 2 deletions
@@ -27,6 +27,11 @@ Release date: - Close #1824 + * Fix false-positive ``undefined-variable`` for generated + comprehension variables in function default arguments + + Close #1897 + What's New in Pylint 1.8.2? ========================== diff --git a/doc/whatsnew/1.8.rst b/doc/whatsnew/1.8.rst index 7cf3b354b..d78ac081f 100644 --- a/doc/whatsnew/1.8.rst +++ b/doc/whatsnew/1.8.rst @@ -388,3 +388,6 @@ Other Changes * Fix false positive ``undefined-variable`` for lambda arguments in class definitions + +* Fix false-positive ``undefined-variable`` for generated + comprehension variables in function default arguments diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 4af7f53bb..5eaf255ca 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -1099,8 +1099,12 @@ class VariablesChecker(BaseChecker): # if the name node is used as a function default argument's value or as # a decorator, then start from the parent frame of the function instead # of the function frame - and thus open an inner class scope - if (utils.is_func_default(node) or utils.is_func_decorator(node) - or utils.is_ancestor_name(frame, node)): + if ((utils.is_func_default(node) and not utils.in_comprehension(node)) or + utils.is_func_decorator(node) or utils.is_ancestor_name(frame, node)): + # Do not use the highest scope to look for variable name consumption in this case + # If the name is used in the function default, or as a decorator, then it + # cannot be defined there + # (except for list comprehensions in function defaults) start_index = len(self._to_consume) - 2 else: start_index = len(self._to_consume) - 1 diff --git a/pylint/test/unittest_checker_variables.py b/pylint/test/unittest_checker_variables.py index 1ef4a50d2..1ca08fc22 100644 --- a/pylint/test/unittest_checker_variables.py +++ b/pylint/test/unittest_checker_variables.py @@ -179,6 +179,28 @@ class TestVariablesCheckerWithTearDown(CheckerTestCase): with self.assertNoMessages(): self.walk(node) + def test_scope_in_defaults(self): + # Should not emit undefined variable + node = astroid.extract_node(''' + def foof(x=[i for i in [1, 2]]): + return x + ''') + # Trickier to detect + node = astroid.extract_node(''' + def foof(x=[(i, 1) for i in [1, 2]]): + return x + ''') + with self.assertNoMessages(): + self.walk(node) + # Lambdas have their own scope + node = astroid.extract_node(''' + def foof(x=lambda zoo: zoo): + return x + ''') + with self.assertNoMessages(): + self.walk(node) + + class TestMissingSubmodule(CheckerTestCase): CHECKER_CLASS = variables.VariablesChecker |