Skip to content

Commit

Permalink
Handle annotations import
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielNoord committed Oct 18, 2021
1 parent c45d82e commit 63e34c0
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 6 deletions.
26 changes: 20 additions & 6 deletions pylint/checkers/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -1195,7 +1195,13 @@ def visit_name(self, node: nodes.Name) -> None:
)
elif self._is_only_type_assignment(node, defstmt):
self.add_message("undefined-variable", node=node, args=node.name)
elif self._is_first_level_self_reference(node, defstmt):
elif self._is_first_level_type_annotation_reference(node, defstmt):
if not utils.is_postponed_evaluation_enabled(node):
self.add_message(
"used-before-assignment", node=node, args=node.name
)
break
elif self._is_first_level_default_reference(node, defstmt):
self.add_message(
"used-before-assignment", node=node, args=node.name
)
Expand Down Expand Up @@ -1575,18 +1581,26 @@ def _is_only_type_assignment(node: nodes.Name, defstmt: nodes.Statement) -> bool
return True

@staticmethod
def _is_first_level_self_reference(
def _is_first_level_type_annotation_reference(
node: nodes.Name, defstmt: nodes.Statement
) -> bool:
"""Check if a first level method's annotation or default values
refers to its own class. See tests/functional/u/use/used_before_assignement.py
for additional examples.
"""Check if a first level method's annotation refers to its own class.
See tests/functional/u/use/used_before_assignement.py for additional examples.
"""
if isinstance(defstmt, nodes.ClassDef) and node.frame().parent == defstmt:
# Check if used as type annotation
if isinstance(node.parent, nodes.Arguments):
return True
# Check if used as default value by calling the class
return False

@staticmethod
def _is_first_level_default_reference(
node: nodes.Name, defstmt: nodes.Statement
) -> bool:
"""Check if a first level method's default value refers to its own class.
See tests/functional/u/use/used_before_assignement.py for additional examples.
"""
if isinstance(defstmt, nodes.ClassDef) and node.frame().parent == defstmt:
if isinstance(node.parent, nodes.Call) and isinstance(
node.parent.parent, nodes.Arguments
):
Expand Down
24 changes: 24 additions & 0 deletions tests/functional/u/use/used_before_assignment_py37.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Tests for used-before-assignment with functions added in python 3.7"""
# pylint: disable=missing-function-docstring
from __future__ import annotations


class MyClass:
"""With the future import only default values can't refer to the base class"""

def incorrect_method(self, other: MyClass) -> bool:
return self == other

def second_incorrect_method(
self, other=MyClass() # [used-before-assignment]
) -> bool:
return self == other

def correct_method(self, other: "MyClass") -> bool:
return self == other

def second_correct_method(self) -> bool:
def inner_method(self, other: MyClass) -> bool:
return self == other

return inner_method(self, MyClass())
2 changes: 2 additions & 0 deletions tests/functional/u/use/used_before_assignment_py37.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[testoptions]
min_pyver=3.7
1 change: 1 addition & 0 deletions tests/functional/u/use/used_before_assignment_py37.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
used-before-assignment:13:20:MyClass.second_incorrect_method:Using variable 'MyClass' before assignment:HIGH

0 comments on commit 63e34c0

Please sign in to comment.