Skip to content

Commit

Permalink
Fix false-positive no-member for typed annotations without default value
Browse files Browse the repository at this point in the history
  • Loading branch information
cdce8p authored and Pierre-Sassoulas committed Mar 6, 2021
1 parent a7d37d2 commit 803a252
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 6 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ Release date: TBA

Closes #4180

* Fix false-positive ``no-member`` for typed annotations without default value.

Closes #3167


What's New in Pylint 2.7.2?
===========================
Expand Down
7 changes: 1 addition & 6 deletions pylint/checkers/typecheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,12 +474,7 @@ def _emit_no_member(node, owner, owner_name, ignored_mixins=True, ignored_none=T

# Exclude typed annotations, since these might actually exist
# at some point during the runtime of the program.
attribute = owner.locals.get(node.attrname, [None])[0]
if (
attribute
and isinstance(attribute, astroid.AssignName)
and isinstance(attribute.parent, astroid.AnnAssign)
):
if utils.is_attribute_typed_annotation(owner, node.attrname):
return False
if isinstance(owner, objects.Super):
# Verify if we are dealing with an invalid Super object.
Expand Down
24 changes: 24 additions & 0 deletions pylint/checkers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1435,3 +1435,27 @@ def is_classdef_type(node: astroid.ClassDef) -> bool:
if isinstance(base, astroid.Name) and base.name == "type":
return True
return False


def is_attribute_typed_annotation(
node: Union[astroid.ClassDef, astroid.Instance], attr_name: str
) -> bool:
"""Test if attribute is typed annotation in current node
or any base nodes.
"""
attribute = node.locals.get(attr_name, [None])[0]
if (
attribute
and isinstance(attribute, astroid.AssignName)
and isinstance(attribute.parent, astroid.AnnAssign)
):
return True
for base in node.bases:
inferred = safe_infer(base)
if (
inferred
and isinstance(inferred, astroid.ClassDef)
and is_attribute_typed_annotation(inferred, attr_name)
):
return True
return False
25 changes: 25 additions & 0 deletions tests/functional/m/member_checks_typed_annotations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# pylint: disable=missing-docstring,invalid-name,too-few-public-methods
class A:
myfield: int

class B(A):
pass

class C:
pass

class D(C, B):
pass


a = A()
print(a.myfield)

b = B()
print(b.myfield)

d = D()
print(d.myfield)

c = C()
print(c.myfield) # [no-member]
1 change: 1 addition & 0 deletions tests/functional/m/member_checks_typed_annotations.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
no-member:25:6::Instance of 'C' has no 'myfield' member:INFERENCE

0 comments on commit 803a252

Please sign in to comment.