Skip to content

Commit d9927e4

Browse files
authored
Fix possibly-used-before-assignment false negative for type-annotated assignment (#10437)
1 parent 22ace0f commit d9927e4

File tree

6 files changed

+38
-4
lines changed

6 files changed

+38
-4
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix a false negative for `possibly-used-before-assignment` when a variable is conditionally defined
2+
and later assigned to a type-annotated variable.
3+
4+
Closes #10421

pylint/checkers/variables.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2541,12 +2541,13 @@ def _is_never_evaluated(
25412541
return False
25422542

25432543
@staticmethod
2544-
def _is_variable_annotation_in_function(node: nodes.NodeNG) -> bool:
2545-
is_annotation = utils.get_node_first_ancestor_of_type(node, nodes.AnnAssign)
2544+
def _is_variable_annotation_in_function(node: nodes.Name) -> bool:
2545+
ann_assign = utils.get_node_first_ancestor_of_type(node, nodes.AnnAssign)
25462546
return (
2547-
is_annotation
2547+
ann_assign
2548+
and (node is ann_assign.annotation or ann_assign.annotation.parent_of(node))
25482549
and utils.get_node_first_ancestor_of_type( # type: ignore[return-value]
2549-
is_annotation, nodes.FunctionDef
2550+
ann_assign, nodes.FunctionDef
25502551
)
25512552
)
25522553

tests/functional/u/used/used_before_assignment_scoping.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,10 @@ def func():
1616
first = datetime.now() # [used-before-assignment]
1717
second = datetime.now()
1818
return first, second
19+
20+
21+
def annotated_declaration_with_type_subscript():
22+
dt_list: list[datetime]
23+
dt_list = [datetime.now(), datetime.now()] # [used-before-assignment]
24+
25+
return dt_list
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
used-before-assignment:10:13:10:21:func_two:Using variable 'datetime' before assignment:INFERENCE
22
used-before-assignment:16:12:16:20:func:Using variable 'datetime' before assignment:INFERENCE
3+
used-before-assignment:23:15:23:23:annotated_declaration_with_type_subscript:Using variable 'datetime' before assignment:INFERENCE

tests/functional/u/used/used_before_assignment_type_annotations.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,22 @@ def my_method(self) -> Coords:
8888
pass
8989

9090
print(MyObject)
91+
92+
93+
def conditional_annotated_assignment():
94+
"""Variable is conditionally defined but later used in a type-annotated assignment."""
95+
if object() is None:
96+
data={"cat": "harf"}
97+
token: str = data.get("cat") # [possibly-used-before-assignment]
98+
print(token)
99+
100+
101+
def loop_conditional_annotated_assignment():
102+
"""Variable is conditionally defined inside a for-loop but later used
103+
in a type-annotated assignment.
104+
"""
105+
for _ in range(3):
106+
if object() is None:
107+
data={"cat": "harf"}
108+
token: str = data.get("cat") # [possibly-used-before-assignment]
109+
print(token)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
used-before-assignment:15:10:15:18:only_type_assignment:Using variable 'variable' before assignment:HIGH
22
used-before-assignment:28:10:28:18:value_assignment_after_access:Using variable 'variable' before assignment:HIGH
33
undefined-variable:62:14:62:17:decorator_returning_incorrect_function.wrapper_with_type_and_no_value:Undefined variable 'var':HIGH
4+
possibly-used-before-assignment:97:17:97:21:conditional_annotated_assignment:Possibly using variable 'data' before assignment:CONTROL_FLOW
5+
possibly-used-before-assignment:108:17:108:21:loop_conditional_annotated_assignment:Possibly using variable 'data' before assignment:CONTROL_FLOW

0 commit comments

Comments
 (0)