summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryce Guinta <bryce.paul.guinta@gmail.com>2018-02-26 17:19:51 -0700
committerBryce Guinta <bryce.paul.guinta@gmail.com>2018-03-03 01:10:02 -0700
commit2746fe3a0faaf7bc5cd002af9408460f383d867b (patch)
treea07d111d35a9f2820400ce4e6bc007732c805231
parentfbb93327a3e85a3c4661261c0bd6a0735a53acf7 (diff)
downloadpylint-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--ChangeLog5
-rw-r--r--doc/whatsnew/1.8.rst3
-rw-r--r--pylint/checkers/variables.py8
-rw-r--r--pylint/test/unittest_checker_variables.py22
4 files changed, 36 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index aa08a92f6..64f296049 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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