Skip to content

Commit 2ad8247

Browse files
authored
Merge pull request #4709 from yushao2/fix-unused-private-member-4673
[unused-private-member] add logic for checking nested functions
2 parents 8ceb26d + 1d09bc8 commit 2ad8247

File tree

4 files changed

+65
-3
lines changed

4 files changed

+65
-3
lines changed

ChangeLog

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Release date: TBA
1414

1515
* pyreverse: Show class has-a relationships inferred from the type-hint
1616

17-
Closes #4744
17+
Closes #4744
1818

1919
* Added ``ignored-parents`` option to the design checker to ignore specific
2020
classes from the ``too-many-ancestors`` check (R0901).
@@ -56,6 +56,10 @@ Closes #4744
5656

5757
Closes #3692
5858

59+
* Fix false-positive of ``unused-private-member`` when using nested functions in a class
60+
61+
Closes #4673
62+
5963

6064
What's New in Pylint 2.9.6?
6165
===========================

pylint/checkers/classes.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,13 @@ def _check_unused_private_functions(self, node: astroid.ClassDef) -> None:
909909
function_def = cast(astroid.FunctionDef, function_def)
910910
if not is_attr_private(function_def.name):
911911
continue
912+
parent_scope = function_def.parent.scope()
913+
if isinstance(parent_scope, astroid.FunctionDef):
914+
# Handle nested functions
915+
if function_def.name in (
916+
n.name for n in parent_scope.nodes_of_class(astroid.Name)
917+
):
918+
continue
912919
for attribute in node.nodes_of_class(astroid.Attribute):
913920
attribute = cast(astroid.Attribute, attribute)
914921
if (
@@ -931,11 +938,19 @@ def _check_unused_private_functions(self, node: astroid.ClassDef) -> None:
931938
):
932939
break
933940
else:
934-
function_repr = f"{function_def.name}({function_def.args.as_string()})"
941+
name_stack = []
942+
curr = parent_scope
943+
# Generate proper names for nested functions
944+
while curr != node:
945+
name_stack.append(curr.name)
946+
curr = curr.parent.scope()
947+
948+
outer_level_names = f"{'.'.join(reversed(name_stack))}"
949+
function_repr = f"{outer_level_names}.{function_def.name}({function_def.args.as_string()})"
935950
self.add_message(
936951
"unused-private-member",
937952
node=function_def,
938-
args=(node.name, function_repr),
953+
args=(node.name, function_repr.lstrip(".")),
939954
)
940955

941956
def _check_unused_private_variables(self, node: astroid.ClassDef) -> None:

tests/functional/u/unused/unused_private_member.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,44 @@ def __new__(cls, func, *args):
160160
def exec(self):
161161
print(self.__secret_bool)
162162
return self.func(*self.__args)
163+
164+
165+
# Test cases for false-positive reported in #4673
166+
# https://github.com/PyCQA/pylint/issues/4673
167+
class FalsePositive4673:
168+
""" The testing class """
169+
170+
def __init__(self, in_thing):
171+
self.thing = False
172+
self.do_thing(in_thing)
173+
174+
def do_thing(self, in_thing):
175+
""" Checks the false-positive condition, sets a property. """
176+
def __false_positive(in_thing):
177+
print(in_thing)
178+
179+
def __true_positive(in_thing): # [unused-private-member]
180+
print(in_thing)
181+
182+
__false_positive(in_thing)
183+
self.thing = True
184+
185+
def undo_thing(self):
186+
""" Unsets a property. """
187+
self.thing = False
188+
189+
def complicated_example(self, flag):
190+
def __inner_1():
191+
pass
192+
193+
def __inner_2():
194+
pass
195+
196+
def __inner_3(fn):
197+
return fn
198+
199+
def __inner_4(): # [unused-private-member]
200+
pass
201+
202+
fn_to_return = __inner_1 if flag else __inner_3(__inner_2)
203+
return fn_to_return

tests/functional/u/unused/unused_private_member.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ unused-private-member:34:4:HasUnusedInClass.__test:Unused private member `HasUnu
77
unused-private-member:55:4:HasUnusedInClass.__test_recursive:Unused private member `HasUnusedInClass.__test_recursive(self)`:HIGH
88
unused-private-member:132:8:FalsePositive4657.__init__:Unused private member `FalsePositive4657.__attr_c`:HIGH
99
undefined-variable:137:15:FalsePositive4657.attr_c:Undefined variable 'cls':HIGH
10+
unused-private-member:179:8:FalsePositive4673.do_thing.__true_positive:Unused private member `FalsePositive4673.do_thing.__true_positive(in_thing)`:HIGH
11+
unused-private-member:199:8:FalsePositive4673.complicated_example.__inner_4:Unused private member `FalsePositive4673.complicated_example.__inner_4()`:HIGH

0 commit comments

Comments
 (0)