diff --git a/ChangeLog b/ChangeLog index fd6a991ea8..9f1eca45d2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -321,6 +321,11 @@ Release date: TBA * Update ranges for ``using-constant-test`` and ``missing-parentheses-for-call-in-test`` error messages. +* Don't emit ``no-member`` inside type annotations with + ``from __future__ import annotations``. + + Closes #6594 + .. Insert your changelog randomly, it will reduce merge conflicts (Ie. not necessarily at the end) diff --git a/doc/whatsnew/2.14.rst b/doc/whatsnew/2.14.rst index e780a7f47f..5eb360d21a 100644 --- a/doc/whatsnew/2.14.rst +++ b/doc/whatsnew/2.14.rst @@ -266,6 +266,11 @@ Other Changes * Update ranges for ``using-constant-test`` and ``missing-parentheses-for-call-in-test`` error messages. +* Don't emit ``no-member`` inside type annotations with + ``from __future__ import annotations``. + + Closes #6594 + Deprecations ============ diff --git a/pylint/checkers/typecheck.py b/pylint/checkers/typecheck.py index 8375ec809f..aae5d2935b 100644 --- a/pylint/checkers/typecheck.py +++ b/pylint/checkers/typecheck.py @@ -34,6 +34,7 @@ is_inside_abstract_class, is_iterable, is_mapping, + is_node_in_type_annotation_context, is_overload_stub, is_postponed_evaluation_enabled, is_super, @@ -1039,6 +1040,11 @@ def visit_attribute(self, node: nodes.Attribute) -> None: ): return + if is_postponed_evaluation_enabled(node) and is_node_in_type_annotation_context( + node + ): + return + try: inferred = list(node.expr.infer()) except astroid.InferenceError: diff --git a/tests/functional/g/generated_members.py b/tests/functional/g/generated_members.py index 2b3a2b07db..72878329d8 100644 --- a/tests/functional/g/generated_members.py +++ b/tests/functional/g/generated_members.py @@ -1,6 +1,6 @@ """Test the generated-members config option.""" # pylint: disable=pointless-statement, invalid-name, useless-object-inheritance -from __future__ import print_function +from __future__ import annotations from astroid import nodes from pylint import checkers @@ -18,3 +18,11 @@ class Klass(object): SESSION = Klass() session.rollback() SESSION.rollback() + + +# https://github.com/PyCQA/pylint/issues/6594 +# Don't emit no-member inside type annotations +# with PEP 563 'from __future__ import annotations' +print(Klass.X) # [no-member] +var: "Klass.X" +var2: Klass.X diff --git a/tests/functional/g/generated_members.txt b/tests/functional/g/generated_members.txt index 0e1f71f56f..aac69fb7e8 100644 --- a/tests/functional/g/generated_members.txt +++ b/tests/functional/g/generated_members.txt @@ -1 +1,2 @@ no-member:13:6:13:18::Instance of 'Klass' has no 'spam' member:INFERENCE +no-member:26:6:26:13::Class 'Klass' has no 'X' member:INFERENCE