Skip to content

Commit 4fb4079

Browse files
committed
Fix used-before-assignment false negative with bare type annotations
When a variable has a bare type annotation (e.g. `err: int`) and is only assigned inside except blocks, pylint failed to report used-before-assignment. The bare annotation was treated as a valid definition, suppressing the check. The fix filters bare annotations (AnnAssign without a value) from found_nodes in get_next_to_consume() when there are uncertain definitions (e.g., assignments in except blocks). This ensures the code correctly falls through to _report_unfound_name_definition(). Closes #10847
1 parent d2dc5df commit 4fb4079

4 files changed

Lines changed: 46 additions & 0 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix ``used-before-assignment`` false negative when a variable has a bare type
2+
annotation (without a value) and is only assigned inside ``except`` blocks.
3+
4+
Closes #10847

pylint/checkers/variables.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,19 @@ def get_next_to_consume(self, node: nodes.Name) -> list[nodes.NodeNG] | None:
651651
uncertain_nodes_set = set(uncertain_nodes)
652652
found_nodes = [n for n in found_nodes if n not in uncertain_nodes_set]
653653

654+
# Filter out bare type annotations (AnnAssign without a value) when there
655+
# are uncertain definitions. A bare annotation like `x: int` does not actually
656+
# assign a value, so it should not suppress possibly-used-before-assignment
657+
# when the real assignments are uncertain (e.g., in except blocks).
658+
if found_nodes and self.consumed_uncertain.get(name):
659+
found_nodes = [
660+
n
661+
for n in found_nodes
662+
if not (
663+
isinstance(n.parent, nodes.AnnAssign) and n.parent.value is None
664+
)
665+
]
666+
654667
return found_nodes
655668

656669
def _inferred_to_define_name_raise_or_return(

tests/functional/u/used/used_before_assignment_type_annotations.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,31 @@ def loop_conditional_annotated_assignment():
107107
data={"cat": "harf"}
108108
token: str = data.get("cat") # [possibly-used-before-assignment]
109109
print(token)
110+
111+
112+
def bare_annotation_with_except_assignment():
113+
"""A bare type annotation should not suppress used-before-assignment
114+
when the only real assignments are in except blocks.
115+
116+
https://github.com/pylint-dev/pylint/issues/10847
117+
"""
118+
result = None
119+
err: int # pylint: disable=unused-variable
120+
try:
121+
result = 1
122+
except Exception: # pylint: disable=broad-exception-caught
123+
err = 1
124+
if not result:
125+
print(err) # [used-before-assignment]
126+
127+
128+
def bare_annotation_with_value_and_except():
129+
"""A type annotation with a value should suppress the warning."""
130+
result = None
131+
err: int = 0
132+
try:
133+
result = 1
134+
except Exception: # pylint: disable=broad-exception-caught
135+
err = 1
136+
if not result:
137+
print(err)

tests/functional/u/used/used_before_assignment_type_annotations.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ used-before-assignment:28:10:28:18:value_assignment_after_access:Using variable
33
undefined-variable:62:14:62:17:decorator_returning_incorrect_function.wrapper_with_type_and_no_value:Undefined variable 'var':HIGH
44
possibly-used-before-assignment:97:17:97:21:conditional_annotated_assignment:Possibly using variable 'data' before assignment:CONTROL_FLOW
55
possibly-used-before-assignment:108:17:108:21:loop_conditional_annotated_assignment:Possibly using variable 'data' before assignment:CONTROL_FLOW
6+
used-before-assignment:125:14:125:17:bare_annotation_with_except_assignment:Using variable 'err' before assignment:CONTROL_FLOW

0 commit comments

Comments
 (0)