summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Simmons <a.simmons@deakin.edu.au>2020-04-20 22:40:09 +1000
committerClaudiu Popa <pcmanticore@gmail.com>2020-04-22 08:26:43 +0200
commitd1484608e89c3e7e2900183e9fb9cbc183de1fb5 (patch)
tree947568a7b728feea1df966068c26f547795921ee
parent67d05722b705c949a6bc0638f65e60c5f888c8d4 (diff)
downloadpylint-git-d1484608e89c3e7e2900183e9fb9cbc183de1fb5.tar.gz
Fix false positive for ``undefined-variable`` when using class attribute in decorator (#511)
-rw-r--r--CONTRIBUTORS.txt2
-rw-r--r--ChangeLog4
-rw-r--r--pylint/checkers/variables.py33
-rw-r--r--tests/unittest_checker_variables.py26
4 files changed, 53 insertions, 12 deletions
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index cd15199a8..420c62417 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -379,3 +379,5 @@ contributors:
* Matthew Beckers (mattlbeck): contributor
* Yang Yang: contributor
+
+* Andrew J. Simmons (anjsimmo): contributor
diff --git a/ChangeLog b/ChangeLog
index b794688b5..5f2306f94 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,10 @@ What's New in Pylint 2.5.0?
Release date: TBA
+* Fix a false positive for ``undefined-variable`` when using class attribute in decorator
+
+ Close #511
+
* Remove HTML quoting of messages in JSON output.
Close #2769
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index e93e1b3d0..ac1d176ee 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -1249,16 +1249,20 @@ class VariablesChecker(BaseChecker):
@staticmethod
def _defined_in_function_definition(node, frame):
- in_annotation_or_default = False
+ in_annotation_or_default_or_decorator = False
if isinstance(frame, astroid.FunctionDef) and node.statement() is frame:
- in_annotation_or_default = (
- node in frame.args.annotations
- or node in frame.args.posonlyargs_annotations
- or node in frame.args.kwonlyargs_annotations
- or node is frame.args.varargannotation
- or node is frame.args.kwargannotation
- ) or frame.args.parent_of(node)
- return in_annotation_or_default
+ in_annotation_or_default_or_decorator = (
+ (
+ node in frame.args.annotations
+ or node in frame.args.posonlyargs_annotations
+ or node in frame.args.kwonlyargs_annotations
+ or node is frame.args.varargannotation
+ or node is frame.args.kwargannotation
+ )
+ or frame.args.parent_of(node)
+ or (frame.decorators and frame.decorators.parent_of(node))
+ )
+ return in_annotation_or_default_or_decorator
@staticmethod
def _is_variable_violation(
@@ -1419,13 +1423,18 @@ class VariablesChecker(BaseChecker):
name = node.name
frame = node.statement().scope()
- in_annotation_or_default = self._defined_in_function_definition(node, frame)
- if in_annotation_or_default:
+ in_annotation_or_default_or_decorator = self._defined_in_function_definition(
+ node, frame
+ )
+ if in_annotation_or_default_or_decorator:
frame_locals = frame.parent.scope().locals
else:
frame_locals = frame.locals
return not (
- (isinstance(frame, astroid.ClassDef) or in_annotation_or_default)
+ (
+ isinstance(frame, astroid.ClassDef)
+ or in_annotation_or_default_or_decorator
+ )
and name in frame_locals
)
diff --git a/tests/unittest_checker_variables.py b/tests/unittest_checker_variables.py
index b594e7d18..f33dbf8af 100644
--- a/tests/unittest_checker_variables.py
+++ b/tests/unittest_checker_variables.py
@@ -119,6 +119,32 @@ class TestVariablesChecker(CheckerTestCase):
with self.assertAddsMessages(msg):
self.checker.visit_global(node)
+ def test_listcomp_in_decorator(self):
+ """ Make sure class attributes in scope for listcomp in decorator.
+
+ https://github.com/PyCQA/pylint/issues/511
+ """
+ module = astroid.parse(
+ """
+ def dec(inp):
+ def inner(func):
+ print(inp)
+ return func
+ return inner
+
+
+ class Cls:
+
+ DATA = "foo"
+
+ @dec([x for x in DATA])
+ def fun(self):
+ pass
+ """
+ )
+ with self.assertNoMessages():
+ self.walk(module)
+
class TestVariablesCheckerWithTearDown(CheckerTestCase):