Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/whatsnew/fragments/10421.false_negative
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix a false negative for `possibly-used-before-assignment` when a variable is conditionally defined
and later assigned to a type-annotated variable.

Closes #10421
9 changes: 5 additions & 4 deletions pylint/checkers/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -2541,12 +2541,13 @@ def _is_never_evaluated(
return False

@staticmethod
def _is_variable_annotation_in_function(node: nodes.NodeNG) -> bool:
is_annotation = utils.get_node_first_ancestor_of_type(node, nodes.AnnAssign)
def _is_variable_annotation_in_function(node: nodes.Name) -> bool:
ann_assign = utils.get_node_first_ancestor_of_type(node, nodes.AnnAssign)
return (
is_annotation
ann_assign
and (node is ann_assign.annotation or ann_assign.annotation.parent_of(node))
and utils.get_node_first_ancestor_of_type( # type: ignore[return-value]
is_annotation, nodes.FunctionDef
ann_assign, nodes.FunctionDef
)
)

Expand Down
7 changes: 7 additions & 0 deletions tests/functional/u/used/used_before_assignment_scoping.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,10 @@ def func():
first = datetime.now() # [used-before-assignment]
second = datetime.now()
return first, second


def annotated_declaration_with_type_subscript():
dt_list: list[datetime]
dt_list = [datetime.now(), datetime.now()] # [used-before-assignment]

return dt_list
1 change: 1 addition & 0 deletions tests/functional/u/used/used_before_assignment_scoping.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
used-before-assignment:10:13:10:21:func_two:Using variable 'datetime' before assignment:INFERENCE
used-before-assignment:16:12:16:20:func:Using variable 'datetime' before assignment:INFERENCE
used-before-assignment:23:15:23:23:annotated_declaration_with_type_subscript:Using variable 'datetime' before assignment:INFERENCE
19 changes: 19 additions & 0 deletions tests/functional/u/used/used_before_assignment_type_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,22 @@ def my_method(self) -> Coords:
pass

print(MyObject)


def conditional_annotated_assignment():
"""Variable is conditionally defined but later used in a type-annotated assignment."""
if object() is None:
data={"cat": "harf"}
token: str = data.get("cat") # [possibly-used-before-assignment]
print(token)


def loop_conditional_annotated_assignment():
"""Variable is conditionally defined inside a for-loop but later used
in a type-annotated assignment.
"""
for _ in range(3):
if object() is None:
data={"cat": "harf"}
token: str = data.get("cat") # [possibly-used-before-assignment]
print(token)
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
used-before-assignment:15:10:15:18:only_type_assignment:Using variable 'variable' before assignment:HIGH
used-before-assignment:28:10:28:18:value_assignment_after_access:Using variable 'variable' before assignment:HIGH
undefined-variable:62:14:62:17:decorator_returning_incorrect_function.wrapper_with_type_and_no_value:Undefined variable 'var':HIGH
possibly-used-before-assignment:97:17:97:21:conditional_annotated_assignment:Possibly using variable 'data' before assignment:CONTROL_FLOW
possibly-used-before-assignment:108:17:108:21:loop_conditional_annotated_assignment:Possibly using variable 'data' before assignment:CONTROL_FLOW