From 482d9c97e5385a73a3460011abeecbea7b9b307a Mon Sep 17 00:00:00 2001 From: Zen Date: Tue, 1 Oct 2024 02:33:41 +0800 Subject: [PATCH] fix(used-before-assignment): resolve false negative for type_checking guard --- pylint/checkers/variables.py | 19 +++++++++++-------- .../u/used/used_before_assignment_scoping.py | 12 +++++++++++- .../u/used/used_before_assignment_scoping.txt | 2 ++ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py index a996dc3277..3352c17c2a 100644 --- a/pylint/checkers/variables.py +++ b/pylint/checkers/variables.py @@ -1787,12 +1787,12 @@ def _check_consumer( if found_nodes is None: return (VariableVisitConsumerAction.CONTINUE, None) if not found_nodes: - self._report_unfound_name_definition(node, current_consumer) + is_reported = self._report_unfound_name_definition(node, current_consumer) # Mark for consumption any nodes added to consumed_uncertain by # get_next_to_consume() because they might not have executed. nodes_to_consume = current_consumer.consumed_uncertain[node.name] nodes_to_consume = self._filter_type_checking_import_from_consumption( - node, nodes_to_consume + node, nodes_to_consume, is_reported ) return ( VariableVisitConsumerAction.RETURN, @@ -1966,7 +1966,7 @@ def _report_unfound_name_definition( self, node: nodes.NodeNG, current_consumer: NamesConsumer, - ) -> None: + ) -> bool: """Reports used-before-assignment when all name definition nodes get filtered out by NamesConsumer. """ @@ -1974,16 +1974,16 @@ def _report_unfound_name_definition( self._postponed_evaluation_enabled and utils.is_node_in_type_annotation_context(node) ): - return + return False if self._is_builtin(node.name): - return + return False if self._is_variable_annotation_in_function(node): - return + return False if ( node.name in self._evaluated_type_checking_scopes and node.scope() in self._evaluated_type_checking_scopes[node.name] ): - return + return False confidence = HIGH if node.name in current_consumer.names_under_always_false_test: @@ -2003,10 +2003,13 @@ def _report_unfound_name_definition( confidence=confidence, ) + return True + def _filter_type_checking_import_from_consumption( self, node: nodes.NodeNG, nodes_to_consume: list[nodes.NodeNG], + is_reported: bool ) -> list[nodes.NodeNG]: """Do not consume type-checking import node as used-before-assignment may invoke in different scopes. @@ -2022,7 +2025,7 @@ def _filter_type_checking_import_from_consumption( ) # If used-before-assignment reported for usage of type checking import # keep track of its scope - if type_checking_import and not self._is_variable_annotation_in_function(node): + if type_checking_import and is_reported: self._evaluated_type_checking_scopes.setdefault(node.name, []).append( node.scope() ) diff --git a/tests/functional/u/used/used_before_assignment_scoping.py b/tests/functional/u/used/used_before_assignment_scoping.py index 0d88210bdd..69fa3c2b95 100644 --- a/tests/functional/u/used/used_before_assignment_scoping.py +++ b/tests/functional/u/used/used_before_assignment_scoping.py @@ -1,5 +1,6 @@ -# pylint: disable=missing-function-docstring, missing-module-docstring +# pylint: disable=missing-function-docstring, missing-module-docstring, unused-variable, too-few-public-methods, no-member, missing-class-docstring +from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -16,3 +17,12 @@ def func(): first = datetime.now() # [used-before-assignment] second = datetime.now() return first, second + + +def x() -> datetime.datetime: # this is good + return datetime.datetime.now() # [used-before-assignment] + + +class Z: + def something(self) -> None: + z = datetime.datetime.now() # [used-before-assignment] diff --git a/tests/functional/u/used/used_before_assignment_scoping.txt b/tests/functional/u/used/used_before_assignment_scoping.txt index 007f59a27c..78d56c14b5 100644 --- a/tests/functional/u/used/used_before_assignment_scoping.txt +++ b/tests/functional/u/used/used_before_assignment_scoping.txt @@ -1,2 +1,4 @@ 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:11:23:19:x:Using variable 'datetime' before assignment:INFERENCE +used-before-assignment:28:12:28:20:Z.something:Using variable 'datetime' before assignment:INFERENCE