From 89d9e65272078443ce4cec8067a8ffaa6288be0f Mon Sep 17 00:00:00 2001 From: andrecs <12188364+andrecsilva@users.noreply.github.com> Date: Tue, 2 Jan 2024 13:46:18 -0300 Subject: [PATCH 1/2] Added expression propagation for literal-or-new-object-identity --- src/core_codemods/literal_or_new_object_identity.py | 7 ++++--- .../codemods/test_literal_or_new_object_identity.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/core_codemods/literal_or_new_object_identity.py b/src/core_codemods/literal_or_new_object_identity.py index fe98b157..e6d9f73b 100644 --- a/src/core_codemods/literal_or_new_object_identity.py +++ b/src/core_codemods/literal_or_new_object_identity.py @@ -2,10 +2,10 @@ from codemodder.codemods.api import BaseCodemod from codemodder.codemods.base_codemod import ReviewGuidance -from codemodder.codemods.utils_mixin import NameResolutionMixin +from codemodder.codemods.utils_mixin import NameAndAncestorResolutionMixin -class LiteralOrNewObjectIdentity(BaseCodemod, NameResolutionMixin): +class LiteralOrNewObjectIdentity(BaseCodemod, NameAndAncestorResolutionMixin): NAME = "literal-or-new-object-identity" SUMMARY = "Replaces is operator with == for literal or new object comparisons" REVIEW_GUIDANCE = ReviewGuidance.MERGE_WITHOUT_REVIEW @@ -40,7 +40,8 @@ def leave_Comparison( left=left, comparisons=[cst.ComparisonTarget() as target] ): if isinstance(target.operator, cst.Is | cst.IsNot): - right = target.comparator + left = self.resolve_expression(left) + right = self.resolve_expression(target.comparator) if self._is_object_creation_or_literal( left ) or self._is_object_creation_or_literal(right): diff --git a/tests/codemods/test_literal_or_new_object_identity.py b/tests/codemods/test_literal_or_new_object_identity.py index 72f53cca..fd165f15 100644 --- a/tests/codemods/test_literal_or_new_object_identity.py +++ b/tests/codemods/test_literal_or_new_object_identity.py @@ -19,6 +19,18 @@ def test_list(self, tmpdir): self.run_and_assert(tmpdir, dedent(input_code), dedent(expected)) assert len(self.file_context.codemod_changes) == 1 + def test_list_indirect(self, tmpdir): + input_code = """\ + some_list = [1,2,3] + l is some_list + """ + expected = """\ + some_list = [1,2,3] + l == some_list + """ + self.run_and_assert(tmpdir, dedent(input_code), dedent(expected)) + assert len(self.file_context.codemod_changes) == 1 + def test_list_lhs(self, tmpdir): input_code = """\ [1,2,3] is l From 9092ffed6cd5451431bac41dd571cee6f0fe8409 Mon Sep 17 00:00:00 2001 From: andrecs <12188364+andrecsilva@users.noreply.github.com> Date: Tue, 2 Jan 2024 14:05:41 -0300 Subject: [PATCH 2/2] Fixed a bug with assignment detection --- src/codemodder/codemods/utils_mixin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/codemodder/codemods/utils_mixin.py b/src/codemodder/codemods/utils_mixin.py index 18c66570..3c80ca59 100644 --- a/src/codemodder/codemods/utils_mixin.py +++ b/src/codemodder/codemods/utils_mixin.py @@ -184,7 +184,7 @@ def find_global_scope(self): def find_single_assignment( self, node: Union[cst.Name, cst.Attribute, cst.Call, cst.Subscript, cst.Decorator], - ) -> Optional[Assignment]: + ) -> Optional[BaseAssignment]: """ Given a MetadataWrapper and a CSTNode representing an access, find if there is a single assignment that it refers to. """ @@ -395,7 +395,7 @@ def resolve_expression(self, node: cst.BaseExpression) -> cst.BaseExpression: def _resolve_name_transitive(self, node: cst.Name) -> Optional[cst.BaseExpression]: maybe_assignment = self.find_single_assignment(node) - if maybe_assignment: + if maybe_assignment and isinstance(maybe_assignment, Assignment): if maybe_target_assignment := self.is_target_of_assignment( maybe_assignment.node ):