Skip to content

Commit a03b6e7

Browse files
jacobtylerwallsPierre-Sassoulas
authored andcommitted
Prevent used-before-assignment for assignment via nonlocal after type annotation (#6185)
1 parent 0741313 commit a03b6e7

File tree

5 files changed

+56
-0
lines changed

5 files changed

+56
-0
lines changed

ChangeLog

+5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ Release date: TBA
4040
* functions & classes which contain both a docstring and an ellipsis.
4141
* A body which contains an ellipsis ``nodes.Expr`` node & at least one other statement.
4242

43+
* Fix false positive for ``used-before-assignment`` for assignments taking place via
44+
nonlocal declarations after an earlier type annotation.
45+
46+
Closes #5394
47+
4348
* Fix crash for ``redefined-slots-in-subclass`` when the type of the slot is not a const or a string.
4449

4550
Closes #6100

doc/whatsnew/2.13.rst

+5
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,11 @@ Other Changes
576576

577577
Closes #5803
578578

579+
* Fix false positive for ``used-before-assignment`` for assignments taking place via
580+
nonlocal declarations after an earlier type annotation.
581+
582+
Closes #5394
583+
579584
* Fix false positive for 'nonexistent-operator' when repeated '-' are
580585
separated (e.g. by parens).
581586

pylint/checkers/variables.py

+14
Original file line numberDiff line numberDiff line change
@@ -2074,6 +2074,20 @@ def _is_only_type_assignment(node: nodes.Name, defstmt: nodes.Statement) -> bool
20742074
parent = node
20752075
while parent is not defstmt_frame.parent:
20762076
parent_scope = parent.scope()
2077+
2078+
# Find out if any nonlocals receive values in nested functions
2079+
for inner_func in parent_scope.nodes_of_class(nodes.FunctionDef):
2080+
if inner_func is parent_scope:
2081+
continue
2082+
if any(
2083+
node.name in nl.names
2084+
for nl in inner_func.nodes_of_class(nodes.Nonlocal)
2085+
) and any(
2086+
node.name == an.name
2087+
for an in inner_func.nodes_of_class(nodes.AssignName)
2088+
):
2089+
return False
2090+
20772091
local_refs = parent_scope.locals.get(node.name, [])
20782092
for ref_node in local_refs:
20792093
# If local ref is in the same frame as our node, but on a later lineno

tests/functional/u/used/used_before_assignment_nonlocal.py

+31
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,34 @@ def on_click(event):
5858
on_click(True)
5959

6060
nonlocal_in_ifexp()
61+
62+
63+
def type_annotation_only_gets_value_via_nonlocal():
64+
"""https://github.com/PyCQA/pylint/issues/5394"""
65+
some_num: int
66+
def inner():
67+
nonlocal some_num
68+
some_num = 5
69+
inner()
70+
print(some_num)
71+
72+
73+
def type_annotation_only_gets_value_via_nonlocal_nested():
74+
"""Similar, with nesting"""
75+
some_num: int
76+
def inner():
77+
def inner2():
78+
nonlocal some_num
79+
some_num = 5
80+
inner2()
81+
inner()
82+
print(some_num)
83+
84+
85+
def type_annotation_never_gets_value_despite_nonlocal():
86+
"""Type annotation lacks a value despite nonlocal declaration"""
87+
some_num: int
88+
def inner():
89+
nonlocal some_num
90+
inner()
91+
print(some_num) # [used-before-assignment]

tests/functional/u/used/used_before_assignment_nonlocal.txt

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ used-before-assignment:30:20:30:30:test_fail3:Using variable 'test_fail4' before
44
used-before-assignment:34:22:34:32:test_fail4:Using variable 'test_fail5' before assignment:HIGH
55
used-before-assignment:34:44:34:53:test_fail4:Using variable 'undefined' before assignment:HIGH
66
used-before-assignment:40:18:40:28:test_fail5:Using variable 'undefined1' before assignment:HIGH
7+
used-before-assignment:91:10:91:18:type_annotation_never_gets_value_despite_nonlocal:Using variable 'some_num' before assignment:HIGH

0 commit comments

Comments
 (0)