diff --git a/CHANGELOG.md b/CHANGELOG.md index b3e7311ec..cd8e98f42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - #895 patchedast Starred and keyword now consumes their syntactically expected and ** (@lieryan) - #896 patchedast cleanup and refactoring (@lieryan) +- #862, #874 Recognize type alias statements as assignments in rename refactoring (@TanbirRamim) # Release 1.15.0 @@ -13,9 +14,6 @@ - #623, #819, #863 Support MatchOr, MatchSequence, MatchStar (@jheld, @lieryan) - #870 Add default implementation for is_dir() (@lieryan) - #872 Fix unicode handling in patchedast (@lieryan) -- #845 Support PEP 695 type parameters on class definitions and type aliases in patchedast (@marlon-costa-dc) -- #849 docs: resolve warnings and update Github Actions versions (@mcepl) -- #835 Tear down tests using python from venv (@aeliton) # Release 1.14.0 diff --git a/rope/base/pyobjectsdef.py b/rope/base/pyobjectsdef.py index 95bd15691..e07f69f3e 100644 --- a/rope/base/pyobjectsdef.py +++ b/rope/base/pyobjectsdef.py @@ -371,6 +371,12 @@ def _Assign(self, node): self.visit(child_node) _ExpressionVisitor(self.scope_visitor).visit(node.value) + def _TypeAlias(self, node): + # the alias value is not recorded as an assigned value: a type + # alias is a distinct TypeAliasType object whose value is evaluated + # lazily, so substituting the value for the alias name is unsafe + self.visit(node.name) + def _assigned(self, name, assignment=None): self.scope_visitor._assigned(name, assignment) @@ -451,6 +457,9 @@ def _Assign(self, node): def _AnnAssign(self, node): _AnnAssignVisitor(self).visit(node) + def _TypeAlias(self, node): + _AssignVisitor(self).visit(node) + def _AugAssign(self, node): pass diff --git a/ropetest/refactor/inlinetest.py b/ropetest/refactor/inlinetest.py index 585214b41..79d807aaf 100644 --- a/ropetest/refactor/inlinetest.py +++ b/ropetest/refactor/inlinetest.py @@ -42,6 +42,37 @@ def test_empty_case(self): refactored = self._inline(code, code.index("a_var") + 1) self.assertEqual("", refactored) + @testutils.only_for_versions_higher("3.12") + def test_inlining_type_alias_is_refused(self): + code = dedent("""\ + type an_alias = int + value = an_alias.__value__ + """) + with self.assertRaises(rope.base.exceptions.RefactoringError): + self._inline(code, code.index("an_alias") + 1) + + @testutils.only_for_versions_higher("3.12") + def test_inlining_lazily_evaluated_type_alias_is_refused(self): + code = dedent("""\ + type an_alias = Later + def a_func(param: an_alias): + pass + class Later: + pass + """) + with self.assertRaises(rope.base.exceptions.RefactoringError): + self._inline(code, code.index("an_alias") + 1) + + @testutils.only_for_versions_higher("3.12") + def test_inlining_generic_type_alias(self): + code = dedent("""\ + type an_alias[T] = list[T] + def a_func(param: an_alias[int]): + pass + """) + with self.assertRaises(rope.base.exceptions.RefactoringError): + self._inline(code, code.index("an_alias") + 1) + def test_long_definition(self): code = dedent("""\ a_var = 10 + (10 + 10) diff --git a/ropetest/refactor/renametest.py b/ropetest/refactor/renametest.py index 9303fb15b..764750ee9 100644 --- a/ropetest/refactor/renametest.py +++ b/ropetest/refactor/renametest.py @@ -242,6 +242,74 @@ def test_renaming_inline_assignment(self): refactored, ) + @testutils.only_for_versions_higher("3.12") + def test_renaming_type_alias(self): + code = dedent("""\ + type old_name = int + x: old_name = 1 + """) + refactored = self._local_rename(code, code.index("old_name") + 1, "new_name") + self.assertEqual( + dedent("""\ + type new_name = int + x: new_name = 1 + """), + refactored, + ) + + @testutils.only_for_versions_higher("3.12") + def test_renaming_type_alias_from_its_usage(self): + code = dedent("""\ + type old_name = int + def a_func(param: old_name) -> old_name: + pass + """) + offset = code.rindex("old_name") + 1 + refactored = self._local_rename(code, offset, "new_name") + self.assertEqual( + dedent("""\ + type new_name = int + def a_func(param: new_name) -> new_name: + pass + """), + refactored, + ) + + @testutils.only_for_versions_higher("3.12") + def test_renaming_type_alias_in_function_scope(self): + code = dedent("""\ + old_name = 1 + def a_func(): + type old_name = int + x: old_name = 1 + """) + offset = code.index("old_name", code.index("type")) + 1 + refactored = self._local_rename(code, offset, "new_name") + self.assertEqual( + dedent("""\ + old_name = 1 + def a_func(): + type new_name = int + x: new_name = 1 + """), + refactored, + ) + + @testutils.only_for_versions_higher("3.12") + def test_renaming_generic_type_alias(self): + code = dedent("""\ + type OldAlias[T] = list[T] + x: OldAlias[int] = [] + """) + refactored = self._local_rename(code, code.index("OldAlias") + 1, "NewAlias") + self.assertEqual( + dedent("""\ + type NewAlias[T] = list[T] + x: NewAlias[int] = [] + """), + refactored, + ) + def test_renaming_arguments_for_normal_args_changing_calls(self): code = dedent("""\ def a_func(p1=None, p2=None):