From 79d208083b83beb9f3dc43679f14061cc5ad0f3e Mon Sep 17 00:00:00 2001 From: LW Date: Mon, 16 Oct 2023 21:31:17 -0700 Subject: [PATCH] fix(compare_singleton_primitives_by_is): compare Name directly instead of QualifiedName (#391) fixes #378 and #375 which are caused by https://github.com/Instagram/LibCST/issues/389. Qualifying a name will never make some thing that wasn't already True/False/None into a True/False/None: ```python import libcst as cst from libcst.metadata.name_provider import QualifiedNameProvider from textwrap import dedent wrapper = cst.MetadataWrapper( cst.parse_module(dedent( ''' x = None x() ''' )) ) x_ref = wrapper.module.body[1].body[0].value print(wrapper.resolve(QualifiedNameProvider)[x_ref]()) ``` prints: ```python {QualifiedName(name='x', source=)} ``` Co-authored-by: Amethyst Reese --- .../compare_singleton_primitives_by_is.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/fixit/rules/compare_singleton_primitives_by_is.py b/src/fixit/rules/compare_singleton_primitives_by_is.py index a2f589a0..c782bd1e 100644 --- a/src/fixit/rules/compare_singleton_primitives_by_is.py +++ b/src/fixit/rules/compare_singleton_primitives_by_is.py @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. -from typing import FrozenSet, Union +from typing import cast, FrozenSet, Union import libcst as cst from libcst.metadata import QualifiedName, QualifiedNameProvider, QualifiedNameSource @@ -37,6 +37,8 @@ class CompareSingletonPrimitivesByIs(LintRule): Valid("False is x"), Valid("x == 2"), Valid("2 != x"), + Valid('"True" == "True"'), + Valid('"True" != "False".lower()'), ] INVALID = [ Invalid( @@ -76,6 +78,14 @@ class CompareSingletonPrimitivesByIs(LintRule): } ) + def is_singleton(self, node: cst.BaseExpression): + qual_name = cast(set, self.get_metadata(QualifiedNameProvider, node, set())) + return ( + isinstance(node, cst.Name) + and qual_name + and qual_name < self.QUALIFIED_SINGLETON_PRIMITIVES + ) + def visit_Comparison(self, node: cst.Comparison) -> None: # Initialize the needs_report flag as False to begin with needs_report = False @@ -84,12 +94,7 @@ def visit_Comparison(self, node: cst.Comparison) -> None: for target in node.comparisons: operator, right_comp = target.operator, target.comparator if isinstance(operator, (cst.Equal, cst.NotEqual)) and ( - not self.QUALIFIED_SINGLETON_PRIMITIVES.isdisjoint( - self.get_metadata(QualifiedNameProvider, left_comp, set()) - ) - or not self.QUALIFIED_SINGLETON_PRIMITIVES.isdisjoint( - self.get_metadata(QualifiedNameProvider, right_comp, set()) - ) + self.is_singleton(left_comp) or self.is_singleton(right_comp) ): needs_report = True altered_comparisons.append(