Skip to content

Commit 4c5272e

Browse files
committed
fix(used-before-assignment): resolve false negative for type_checking guard
1 parent b9cc6b2 commit 4c5272e

File tree

3 files changed

+26
-11
lines changed

3 files changed

+26
-11
lines changed

pylint/checkers/variables.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,12 +1787,12 @@ def _check_consumer(
17871787
if found_nodes is None:
17881788
return (VariableVisitConsumerAction.CONTINUE, None)
17891789
if not found_nodes:
1790-
self._report_unfound_name_definition(node, current_consumer)
1790+
is_reported = self._report_unfound_name_definition(node, current_consumer)
17911791
# Mark for consumption any nodes added to consumed_uncertain by
17921792
# get_next_to_consume() because they might not have executed.
17931793
nodes_to_consume = current_consumer.consumed_uncertain[node.name]
17941794
nodes_to_consume = self._filter_type_checking_import_from_consumption(
1795-
node, nodes_to_consume
1795+
node, nodes_to_consume, is_reported
17961796
)
17971797
return (
17981798
VariableVisitConsumerAction.RETURN,
@@ -1966,24 +1966,24 @@ def _report_unfound_name_definition(
19661966
self,
19671967
node: nodes.NodeNG,
19681968
current_consumer: NamesConsumer,
1969-
) -> None:
1969+
) -> bool:
19701970
"""Reports used-before-assignment when all name definition nodes
19711971
get filtered out by NamesConsumer.
19721972
"""
19731973
if (
19741974
self._postponed_evaluation_enabled
19751975
and utils.is_node_in_type_annotation_context(node)
19761976
):
1977-
return
1977+
return False
19781978
if self._is_builtin(node.name):
1979-
return
1979+
return False
19801980
if self._is_variable_annotation_in_function(node):
1981-
return
1981+
return False
19821982
if (
19831983
node.name in self._evaluated_type_checking_scopes
19841984
and node.scope() in self._evaluated_type_checking_scopes[node.name]
19851985
):
1986-
return
1986+
return False
19871987

19881988
confidence = HIGH
19891989
if node.name in current_consumer.names_under_always_false_test:
@@ -2003,10 +2003,13 @@ def _report_unfound_name_definition(
20032003
confidence=confidence,
20042004
)
20052005

2006+
return True
2007+
20062008
def _filter_type_checking_import_from_consumption(
20072009
self,
20082010
node: nodes.NodeNG,
20092011
nodes_to_consume: list[nodes.NodeNG],
2012+
is_reported: bool
20102013
) -> list[nodes.NodeNG]:
20112014
"""Do not consume type-checking import node as used-before-assignment
20122015
may invoke in different scopes.
@@ -2022,7 +2025,7 @@ def _filter_type_checking_import_from_consumption(
20222025
)
20232026
# If used-before-assignment reported for usage of type checking import
20242027
# keep track of its scope
2025-
if type_checking_import and not self._is_variable_annotation_in_function(node):
2028+
if type_checking_import and is_reported:
20262029
self._evaluated_type_checking_scopes.setdefault(node.name, []).append(
20272030
node.scope()
20282031
)

tests/functional/u/used/used_before_assignment_scoping.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
# pylint: disable=missing-function-docstring, missing-module-docstring
1+
# pylint: disable=missing-function-docstring, missing-module-docstring, unused-variable, too-few-public-methods, no-member, missing-class-docstring
22

3+
from __future__ import annotations
34
from typing import TYPE_CHECKING
45

56
if TYPE_CHECKING:
@@ -16,3 +17,12 @@ def func():
1617
first = datetime.now() # [used-before-assignment]
1718
second = datetime.now()
1819
return first, second
20+
21+
22+
def x() -> datetime.datetime: # this is good
23+
return datetime.datetime.now() # [used-before-assignment]
24+
25+
26+
class Z:
27+
def something(self) -> None:
28+
z = datetime.datetime.now() # [used-before-assignment]
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
used-before-assignment:10:13:10:21:func_two:Using variable 'datetime' before assignment:INFERENCE
2-
used-before-assignment:16:12:16:20:func:Using variable 'datetime' before assignment:INFERENCE
1+
used-before-assignment:11:13:11:21:func_two:Using variable 'datetime' before assignment:INFERENCE
2+
used-before-assignment:17:12:17:20:func:Using variable 'datetime' before assignment:INFERENCE
3+
used-before-assignment:23:11:23:19:x:Using variable 'datetime' before assignment:INFERENCE
4+
used-before-assignment:28:12:28:20:Z.something:Using variable 'datetime' before assignment:INFERENCE

0 commit comments

Comments
 (0)