Skip to content

Commit

Permalink
Prevent used-before-assignment for assignment via nonlocal after ty…
Browse files Browse the repository at this point in the history
…pe annotation (#6185)
  • Loading branch information
jacobtylerwalls committed Apr 6, 2022
1 parent 3047faf commit 0e7aec3
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 0 deletions.
5 changes: 5 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ Release date: TBA
* functions & classes which contain both a docstring and an ellipsis.
* A body which contains an ellipsis ``nodes.Expr`` node & at least one other statement.

* Fix false positive for ``used-before-assignment`` for assignments taking place via
nonlocal declarations after an earlier type annotation.

Closes #5394

* Fix crash for ``redefined-slots-in-subclass`` when the type of the slot is not a const or a string.

Closes #6100
Expand Down
5 changes: 5 additions & 0 deletions doc/whatsnew/2.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,11 @@ Other Changes

Closes #5803

* Fix false positive for ``used-before-assignment`` for assignments taking place via
nonlocal declarations after an earlier type annotation.

Closes #5394

* Fix false positive for 'nonexistent-operator' when repeated '-' are
separated (e.g. by parens).

Expand Down
14 changes: 14 additions & 0 deletions pylint/checkers/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -2079,6 +2079,20 @@ def _is_only_type_assignment(node: nodes.Name, defstmt: nodes.Statement) -> bool
parent = node
while parent is not defstmt_frame.parent:
parent_scope = parent.scope()

# Find out if any nonlocals receive values in nested functions
for inner_func in parent_scope.nodes_of_class(nodes.FunctionDef):
if inner_func is parent_scope:
continue
if any(
node.name in nl.names
for nl in inner_func.nodes_of_class(nodes.Nonlocal)
) and any(
node.name == an.name
for an in inner_func.nodes_of_class(nodes.AssignName)
):
return False

local_refs = parent_scope.locals.get(node.name, [])
for ref_node in local_refs:
# If local ref is in the same frame as our node, but on a later lineno
Expand Down
31 changes: 31 additions & 0 deletions tests/functional/u/used/used_before_assignment_nonlocal.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,34 @@ def on_click(event):
on_click(True)

nonlocal_in_ifexp()


def type_annotation_only_gets_value_via_nonlocal():
"""https://github.com/PyCQA/pylint/issues/5394"""
some_num: int
def inner():
nonlocal some_num
some_num = 5
inner()
print(some_num)


def type_annotation_only_gets_value_via_nonlocal_nested():
"""Similar, with nesting"""
some_num: int
def inner():
def inner2():
nonlocal some_num
some_num = 5
inner2()
inner()
print(some_num)


def type_annotation_never_gets_value_despite_nonlocal():
"""Type annotation lacks a value despite nonlocal declaration"""
some_num: int
def inner():
nonlocal some_num
inner()
print(some_num) # [used-before-assignment]
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ used-before-assignment:30:20:30:30:test_fail3:Using variable 'test_fail4' before
used-before-assignment:34:22:34:32:test_fail4:Using variable 'test_fail5' before assignment:HIGH
used-before-assignment:34:44:34:53:test_fail4:Using variable 'undefined' before assignment:HIGH
used-before-assignment:40:18:40:28:test_fail5:Using variable 'undefined1' before assignment:HIGH
used-before-assignment:91:10:91:18:type_annotation_never_gets_value_despite_nonlocal:Using variable 'some_num' before assignment:HIGH

0 comments on commit 0e7aec3

Please sign in to comment.