diff options
author | Pierre Sassoulas <pierre.sassoulas@gmail.com> | 2021-06-30 22:32:53 +0200 |
---|---|---|
committer | Pierre Sassoulas <pierre.sassoulas@gmail.com> | 2021-06-30 23:13:20 +0200 |
commit | aa6d91f45407544e49287ca5b43d6c86d317f5a7 (patch) | |
tree | 50fb2ac887d9e35890a29f48e5be804a4e4a863c | |
parent | efc35c3c40622f419ea07f698c029f13fb9fed52 (diff) | |
download | pylint-git-aa6d91f45407544e49287ca5b43d6c86d317f5a7.tar.gz |
[unused-private-member] Handle the case with Call node
As seen in issue #4638
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | pylint/checkers/classes.py | 23 | ||||
-rw-r--r-- | tests/functional/u/unused/unused_private_member.py | 11 |
3 files changed, 34 insertions, 4 deletions
@@ -17,6 +17,10 @@ Release date: TBA .. Put bug fixes that should not wait for a new minor version here +* Fix a crash that happened when analysing code using ``type(self)`` to access + a class attribute in the ``unused-private-member`` checker. + + Closes #4638 What's New in Pylint 2.9.1? diff --git a/pylint/checkers/classes.py b/pylint/checkers/classes.py index 6fffd2b16..a85e216c5 100644 --- a/pylint/checkers/classes.py +++ b/pylint/checkers/classes.py @@ -909,14 +909,29 @@ a metaclass class method.", continue for attribute in node.nodes_of_class(astroid.Attribute): attribute = cast(astroid.Attribute, attribute) - if attribute.expr is None: # attribute.expr is probably not really an Optional ? + if attribute.expr is None: + # attribute.expr is an Optional[NodeNG] it can be None continue if ( - attribute.attrname == function_def.name - and attribute.scope() != function_def # We ignore recursive calls - and attribute.expr.name in ("self", node.name) + attribute.attrname != function_def.name + or attribute.scope() == function_def ): + # We ignore recursive calls + continue + if isinstance(attribute.expr, astroid.Name) and attribute.expr.name in ( + "self", + node.name, + ): + # self.__attrname / node_name.__attrname break + if isinstance(attribute.expr, astroid.Call): + # type(self).__attrname + inferred = safe_infer(attribute.expr) + if ( + isinstance(inferred, astroid.ClassDef) + and inferred.name == node.name + ): + break else: function_repr = f"{function_def.name}({function_def.args.as_string()})" self.add_message( diff --git a/tests/functional/u/unused/unused_private_member.py b/tests/functional/u/unused/unused_private_member.py index d65478503..35c5801e4 100644 --- a/tests/functional/u/unused/unused_private_member.py +++ b/tests/functional/u/unused/unused_private_member.py @@ -66,3 +66,14 @@ class MyCls: @classmethod def get_class_var(cls): return cls.__class_var + + +class Bla: + """Regression test for issue 4638""" + + def __init__(self): + type(self).__a() + + @classmethod + def __a(cls): + pass |