diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index 30f87d632b..16cf0c87e9 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -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 ) @@ -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 ): diff --git a/tests/functional/u/use/used_before_assignment_py37.py b/tests/functional/u/use/used_before_assignment_py37.py new file mode 100644 index 0000000000..3a53ff1c24 --- /dev/null +++ b/tests/functional/u/use/used_before_assignment_py37.py @@ -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()) diff --git a/tests/functional/u/use/used_before_assignment_py37.rc b/tests/functional/u/use/used_before_assignment_py37.rc new file mode 100644 index 0000000000..a17bb22daf --- /dev/null +++ b/tests/functional/u/use/used_before_assignment_py37.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.7 diff --git a/tests/functional/u/use/used_before_assignment_py37.txt b/tests/functional/u/use/used_before_assignment_py37.txt new file mode 100644 index 0000000000..ade863cca6 --- /dev/null +++ b/tests/functional/u/use/used_before_assignment_py37.txt @@ -0,0 +1 @@ +used-before-assignment:13:20:MyClass.second_incorrect_method:Using variable 'MyClass' before assignment:HIGH