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/9668.false_positive
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix false positives for `possibly-used-before-assignment` when variables are exhaustively
assigned within a `match` block.

Closes #9668
12 changes: 12 additions & 0 deletions pylint/checkers/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,12 @@ def _inferred_to_define_name_raise_or_return(
if isinstance(node, (nodes.With, nodes.For, nodes.While)):
return NamesConsumer._defines_name_raises_or_returns_recursive(name, node)

if isinstance(node, nodes.Match):
return all(
NamesConsumer._defines_name_raises_or_returns_recursive(name, case)
for case in node.cases
)

if not isinstance(node, nodes.If):
return False

Expand Down Expand Up @@ -723,6 +729,7 @@ def _branch_handles_name(self, name: str, body: Iterable[nodes.NodeNG]) -> bool:
nodes.With,
nodes.For,
nodes.While,
nodes.Match,
),
)
and self._inferred_to_define_name_raise_or_return(name, if_body_stmt)
Expand Down Expand Up @@ -975,6 +982,11 @@ def _defines_name_raises_or_returns_recursive(
and NamesConsumer._defines_name_raises_or_returns_recursive(name, stmt)
):
return True
if isinstance(stmt, nodes.Match):
return all(
NamesConsumer._defines_name_raises_or_returns_recursive(name, case)
for case in stmt.cases
)
return False

@staticmethod
Expand Down
40 changes: 36 additions & 4 deletions tests/functional/u/used/used_before_assignment_py310.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def check_value_if_then_match_return(example: Example, should_check: bool) -> st
case _:
return None

return result # [possibly-used-before-assignment] FALSE POSITIVE
return result

def check_value_if_then_match_raise(example: Example, should_check: bool) -> str | None:
if should_check:
Expand All @@ -47,7 +47,7 @@ def check_value_if_then_match_raise(example: Example, should_check: bool) -> str
case _:
raise ValueError("Not a valid enum")

return result # [possibly-used-before-assignment] FALSE POSITIVE
return result

def check_value_if_then_match_assert_never(example: Example, should_check: bool) -> str | None:
if should_check:
Expand All @@ -61,7 +61,7 @@ def check_value_if_then_match_assert_never(example: Example, should_check: bool)
case _:
assert_never(example)

return result # [possibly-used-before-assignment] FALSE POSITIVE
return result

def g(x):
if x is None:
Expand All @@ -73,4 +73,36 @@ def g(x):
case _:
raise TypeError(type(x))

return y # [possibly-used-before-assignment] FALSE POSITIVE
return y

def check_value_if_then_match_nested(
example: Example, example_inner: Example, should_check: bool
) -> str | None:
if should_check:
result = None
else:
match example:
case Example.FOO:
match example_inner:
case Example.BAR:
result = "bar"
case _:
return None
case _:
return None

return result

def check_value_if_then_match_non_exhaustive(example: Example, should_check: bool) -> str | None:
if should_check:
result = None
else:
match example:
case Example.FOO:
result = "foo"
case Example.BAR:
pass
case _:
return None

return result # [possibly-used-before-assignment]
5 changes: 1 addition & 4 deletions tests/functional/u/used/used_before_assignment_py310.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
possibly-used-before-assignment:36:11:36:17:check_value_if_then_match_return:Possibly using variable 'result' before assignment:CONTROL_FLOW
possibly-used-before-assignment:50:11:50:17:check_value_if_then_match_raise:Possibly using variable 'result' before assignment:CONTROL_FLOW
possibly-used-before-assignment:64:11:64:17:check_value_if_then_match_assert_never:Possibly using variable 'result' before assignment:CONTROL_FLOW
possibly-used-before-assignment:76:11:76:12:g:Possibly using variable 'y' before assignment:CONTROL_FLOW
possibly-used-before-assignment:108:11:108:17:check_value_if_then_match_non_exhaustive:Possibly using variable 'result' before assignment:CONTROL_FLOW
Loading