Skip to content

Commit

Permalink
fix(4756): fix false positive unused-private-member for private met…
Browse files Browse the repository at this point in the history
…hods (#5345)

Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
Co-authored-by: Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
  • Loading branch information
3 people committed Mar 15, 2022
1 parent fcc17e9 commit 9755505
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 39 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,10 @@ Release date: 2021-11-24

Closes #4412 #5287

* Fix ``unused-private-member`` false positive when accessing private methods through ``property``.

Closes #4756

* Fix ``install graphiz`` message which isn't needed for puml output format.

* ``MessageTest`` of the unittest ``testutil`` now requires the ``confidence`` attribute
Expand Down
4 changes: 4 additions & 0 deletions doc/whatsnew/2.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ Other Changes

Closes #5722

* Fix ``unused-private-member`` false positive when accessing private methods through ``property``.

Closes #4756

* Fixed crash from ``arguments-differ`` and ``arguments-renamed`` when methods were
defined outside the top level of a class.

Expand Down
44 changes: 25 additions & 19 deletions pylint/checkers/classes/class_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -900,29 +900,35 @@ def _check_unused_private_functions(self, node: nodes.ClassDef) -> None:
n.name for n in parent_scope.nodes_of_class(nodes.Name)
):
continue
for attribute in node.nodes_of_class(nodes.Attribute):
if (
attribute.attrname != function_def.name
or attribute.scope() == function_def # We ignore recursive calls
):
continue
if isinstance(attribute.expr, nodes.Name) and attribute.expr.name in (
"self",
"cls",
node.name,
):
# self.__attrname
# cls.__attrname
# node_name.__attrname
for child in node.nodes_of_class((nodes.Name, nodes.Attribute)):
# Check for cases where the functions are used as a variable instead of as a method call
if isinstance(child, nodes.Name) and child.name == function_def.name:
break
if isinstance(attribute.expr, nodes.Call):
# type(self).__attrname
inferred = safe_infer(attribute.expr)
if isinstance(child, nodes.Attribute):
# Ignore recursive calls
if (
isinstance(inferred, nodes.ClassDef)
and inferred.name == node.name
child.attrname != function_def.name
or child.scope() == function_def
):
continue

# Check self.__attrname, cls.__attrname, node_name.__attrname
if isinstance(child.expr, nodes.Name) and child.expr.name in {
"self",
"cls",
node.name,
}:

break

# Check type(self).__attrname
if isinstance(child.expr, nodes.Call):
inferred = safe_infer(child.expr)
if (
isinstance(inferred, nodes.ClassDef)
and inferred.name == node.name
):
break
else:
name_stack = []
curr = parent_scope
Expand Down
25 changes: 25 additions & 0 deletions tests/functional/u/unused/unused_private_member.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# pylint: disable=missing-docstring, invalid-name, too-few-public-methods, no-self-use, line-too-long, unused-argument, protected-access
from functools import partialmethod


class AnotherClass():
def __test(self): # [unused-private-member]
Expand Down Expand Up @@ -311,6 +313,29 @@ def method(self):
print(self.__class__.__ham)


# https://github.com/PyCQA/pylint/issues/4756
# Check for false positives emitted when private functions are not referenced in the class body
# with standard calls but passed as arguments to other functions.
class FalsePositive4756a:
def __bar(self, x):
print(x)
fizz = partialmethod(__bar, 'fizz')
foo = FalsePositive4756a()
foo.fizz()

class FalsePositive4756b:
def __get_prop(self):
pass

def __set_prop(self, value):
pass

def __del_prop(self):
pass

prop = property(__get_prop, __set_prop, __del_prop)


class TypeSelfCallInMethod:
"""Regression test for issue 5569"""
@classmethod
Expand Down
40 changes: 20 additions & 20 deletions tests/functional/u/unused/unused_private_member.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
unused-private-member:4:4:4:14:AnotherClass.__test:Unused private member `AnotherClass.__test(self)`:UNDEFINED
unused-private-member:8:4:8:15:HasUnusedInClass:Unused private member `HasUnusedInClass.__my_secret`:UNDEFINED
unused-private-member:12:4:12:37:HasUnusedInClass.__private_class_method_unused:Unused private member `HasUnusedInClass.__private_class_method_unused(cls)`:UNDEFINED
unused-private-member:20:4:20:38:HasUnusedInClass.__private_static_method_unused:Unused private member `HasUnusedInClass.__private_static_method_unused()`:UNDEFINED
unused-private-member:28:8:28:30:HasUnusedInClass.__init__:Unused private member `HasUnusedInClass.__instance_secret`:UNDEFINED
unused-private-member:34:4:34:14:HasUnusedInClass.__test:Unused private member `HasUnusedInClass.__test(self, x, y, z)`:UNDEFINED
unused-private-member:55:4:55:24:HasUnusedInClass.__test_recursive:Unused private member `HasUnusedInClass.__test_recursive(self)`:UNDEFINED
unused-private-member:133:8:133:21:FalsePositive4657.__init__:Unused private member `FalsePositive4657.__attr_c`:UNDEFINED
undefined-variable:138:15:138:18:FalsePositive4657.attr_c:Undefined variable 'cls':UNDEFINED
unused-private-member:157:8:157:26:FalsePositive4668.__new__:Unused private member `FalsePositive4668.__unused`:UNDEFINED
unused-private-member:181:8:181:27:FalsePositive4673.do_thing.__true_positive:Unused private member `FalsePositive4673.do_thing.__true_positive(in_thing)`:UNDEFINED
unused-private-member:201:8:201:21:FalsePositive4673.complicated_example.__inner_4:Unused private member `FalsePositive4673.complicated_example.__inner_4()`:UNDEFINED
unused-private-member:212:8:212:23:Crash4755Context.__init__:Unused private member `Crash4755Context.__messages`:UNDEFINED
unused-private-member:229:4:229:24:FalsePositive4681:Unused private member `FalsePositive4681.__should_cause_error`:UNDEFINED
unused-private-member:239:12:239:50:FalsePositive4681.__init__:Unused private member `FalsePositive4681.__should_cause_error`:UNDEFINED
unused-private-member:243:12:243:50:FalsePositive4681.__init__:Unused private member `FalsePositive4681.__should_cause_error`:UNDEFINED
unused-private-member:274:4:274:31:FalsePositive4849.__unused_private_method:Unused private member `FalsePositive4849.__unused_private_method()`:UNDEFINED
unused-private-member:291:4:291:23:Pony.__init_defaults:Unused private member `Pony.__init_defaults(self)`:UNDEFINED
unused-private-member:296:4:296:23:Pony.__get_fur_color:Unused private member `Pony.__get_fur_color(self)`:UNDEFINED
unused-private-member:318:8:318:15:TypeSelfCallInMethod.b:Unused private member `TypeSelfCallInMethod.__a`:UNDEFINED
unused-private-member:6:4:6:14:AnotherClass.__test:Unused private member `AnotherClass.__test(self)`:UNDEFINED
unused-private-member:10:4:10:15:HasUnusedInClass:Unused private member `HasUnusedInClass.__my_secret`:UNDEFINED
unused-private-member:14:4:14:37:HasUnusedInClass.__private_class_method_unused:Unused private member `HasUnusedInClass.__private_class_method_unused(cls)`:UNDEFINED
unused-private-member:22:4:22:38:HasUnusedInClass.__private_static_method_unused:Unused private member `HasUnusedInClass.__private_static_method_unused()`:UNDEFINED
unused-private-member:30:8:30:30:HasUnusedInClass.__init__:Unused private member `HasUnusedInClass.__instance_secret`:UNDEFINED
unused-private-member:36:4:36:14:HasUnusedInClass.__test:Unused private member `HasUnusedInClass.__test(self, x, y, z)`:UNDEFINED
unused-private-member:57:4:57:24:HasUnusedInClass.__test_recursive:Unused private member `HasUnusedInClass.__test_recursive(self)`:UNDEFINED
unused-private-member:135:8:135:21:FalsePositive4657.__init__:Unused private member `FalsePositive4657.__attr_c`:UNDEFINED
undefined-variable:140:15:140:18:FalsePositive4657.attr_c:Undefined variable 'cls':UNDEFINED
unused-private-member:159:8:159:26:FalsePositive4668.__new__:Unused private member `FalsePositive4668.__unused`:UNDEFINED
unused-private-member:183:8:183:27:FalsePositive4673.do_thing.__true_positive:Unused private member `FalsePositive4673.do_thing.__true_positive(in_thing)`:UNDEFINED
unused-private-member:203:8:203:21:FalsePositive4673.complicated_example.__inner_4:Unused private member `FalsePositive4673.complicated_example.__inner_4()`:UNDEFINED
unused-private-member:214:8:214:23:Crash4755Context.__init__:Unused private member `Crash4755Context.__messages`:UNDEFINED
unused-private-member:231:4:231:24:FalsePositive4681:Unused private member `FalsePositive4681.__should_cause_error`:UNDEFINED
unused-private-member:241:12:241:50:FalsePositive4681.__init__:Unused private member `FalsePositive4681.__should_cause_error`:UNDEFINED
unused-private-member:245:12:245:50:FalsePositive4681.__init__:Unused private member `FalsePositive4681.__should_cause_error`:UNDEFINED
unused-private-member:276:4:276:31:FalsePositive4849.__unused_private_method:Unused private member `FalsePositive4849.__unused_private_method()`:UNDEFINED
unused-private-member:293:4:293:23:Pony.__init_defaults:Unused private member `Pony.__init_defaults(self)`:UNDEFINED
unused-private-member:298:4:298:23:Pony.__get_fur_color:Unused private member `Pony.__get_fur_color(self)`:UNDEFINED
unused-private-member:343:8:343:15:TypeSelfCallInMethod.b:Unused private member `TypeSelfCallInMethod.__a`:UNDEFINED

0 comments on commit 9755505

Please sign in to comment.