summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniël van Noord <13665637+DanielNoord@users.noreply.github.com>2021-10-18 14:58:01 +0200
committerDaniël van Noord <13665637+DanielNoord@users.noreply.github.com>2021-10-18 16:03:44 +0200
commit1daee40d6f1cf3d379396dc272c123d987ad4cdd (patch)
tree9aba810d362eade68a6c4715236483183dfc4b8a
parent816c8f8c7badc9355628c3eb0e455650dc1f99ef (diff)
downloadpylint-git-1daee40d6f1cf3d379396dc272c123d987ad4cdd.tar.gz
Make ``undefined-variable`` check line numbers of metaclass parameters
-rw-r--r--ChangeLog5
-rw-r--r--doc/whatsnew/2.12.rst5
-rw-r--r--pylint/checkers/variables.py19
-rw-r--r--tests/functional/u/undefined/undefined_variable_py30.py10
-rw-r--r--tests/functional/u/undefined/undefined_variable_py30.txt1
5 files changed, 34 insertions, 6 deletions
diff --git a/ChangeLog b/ChangeLog
index f9eba4227..f26007d1e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -26,6 +26,11 @@ Release date: TBA
Closes #5140
+* ``undefined-variable`` now correctly considers the line numbering and order of classes
+ used in metaclass declarations
+
+ Closes #4031
+
* Fix bug with importing namespace packages with relative imports
Closes #2967 and #5131
diff --git a/doc/whatsnew/2.12.rst b/doc/whatsnew/2.12.rst
index 54214ac99..be1e5245c 100644
--- a/doc/whatsnew/2.12.rst
+++ b/doc/whatsnew/2.12.rst
@@ -69,3 +69,8 @@ Other Changes
and never get assigned a value
Closes #5140
+
+* ``undefined-variable`` now correctly considers the line numbering and order of classes
+ used in metaclass declarations
+
+ Closes #4031
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index ef53f19cb..1d78184c8 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -2213,23 +2213,30 @@ class VariablesChecker(BaseChecker):
elif metaclass:
name = metaclass.root().name
- found = None
+ found = False
name = METACLASS_NAME_TRANSFORMS.get(name, name)
if name:
# check enclosing scopes starting from most local
for scope_locals, _, _ in self._to_consume[::-1]:
- found = scope_locals.get(name)
- if found:
- consumed.append((scope_locals, name))
+ found_nodes = scope_locals.get(name, [])
+ for found_node in found_nodes:
+ if found_node.lineno <= klass.lineno:
+ consumed.append((scope_locals, name))
+ found = True
+ break
+ # Check parent scope
+ nodes_in_parent_scope = parent_node.locals.get(name, [])
+ for found_node_parent in nodes_in_parent_scope:
+ if found_node_parent.lineno <= klass.lineno:
+ found = True
break
if (
- found is None
+ not found
and not metaclass
and not (
name in nodes.Module.scope_attrs
or utils.is_builtin(name)
or name in self.config.additional_builtins
- or name in parent_node.locals
)
):
self.add_message("undefined-variable", node=klass, args=(name,))
diff --git a/tests/functional/u/undefined/undefined_variable_py30.py b/tests/functional/u/undefined/undefined_variable_py30.py
index 556b1028b..b9b64ec5e 100644
--- a/tests/functional/u/undefined/undefined_variable_py30.py
+++ b/tests/functional/u/undefined/undefined_variable_py30.py
@@ -96,3 +96,13 @@ class MetaClass(type):
class InheritingClass(metaclass=MetaClass, parameter=variable): # [undefined-variable]
pass
+
+
+# Test for #4031
+# https://github.com/PyCQA/pylint/issues/4031
+class Inheritor(metaclass=DefinedTooLate ): # [undefined-variable]
+ pass
+
+
+class DefinedTooLate():
+ pass
diff --git a/tests/functional/u/undefined/undefined_variable_py30.txt b/tests/functional/u/undefined/undefined_variable_py30.txt
index 4756b5cd2..e0a942202 100644
--- a/tests/functional/u/undefined/undefined_variable_py30.txt
+++ b/tests/functional/u/undefined/undefined_variable_py30.txt
@@ -7,3 +7,4 @@ undefined-variable:57:31:FalsePositive342.test_bad2:Undefined variable 'trop2':H
undefined-variable:63:0:Bad:Undefined variable 'ABCMet':HIGH
undefined-variable:66:0:SecondBad:Undefined variable 'ab':HIGH
undefined-variable:97:53:InheritingClass:Undefined variable 'variable':HIGH
+undefined-variable:103:0:Inheritor:Undefined variable 'DefinedTooLate':HIGH