From 5df2698f7738805d31919cb1c50fd4afe80741d4 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Fri, 21 Jul 2017 19:55:45 +0300 Subject: [PATCH] Enable ReplaceWith for type aliases (KT-14929) #KT-14929 Fixed --- .../DeprecatedSymbolUsageFixBase.kt | 27 ++++---- .../ReplaceWithAnnotationAnalyzer.kt | 23 +++++-- ...ndWithDeprecatedArgumentsAndConstructor.kt | 20 ++++++ .../typeAliases/conflictOnTypeAndAlias.kt | 17 +++++ .../typeAliases/constructorUsage.kt | 15 +++++ .../typeAliases/constructorUsage.kt.after | 15 +++++ .../constructorUsageWithConflict.kt | 16 +++++ .../constructorUsageWithConflict1.kt | 12 ++++ .../constructorUsageWithConflict1.kt.after | 12 ++++ .../typeAliases/onlyAliasDeprecated.kt | 8 +++ .../typeAliases/onlyAliasDeprecated.kt.after | 8 +++ .../typeAliases/transitiveFromClass.kt | 12 ++++ .../typeAliases/transitiveLong.kt | 14 +++++ .../typeAliasWithAllGenericParams.kt | 10 +++ .../typeAliasWithAllGenericParams.kt.after | 10 +++ .../idea/quickfix/QuickFixTestGenerated.java | 63 +++++++++++++++++++ 16 files changed, 263 insertions(+), 19 deletions(-) create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/compoundWithDeprecatedArgumentsAndConstructor.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/conflictOnTypeAndAlias.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsage.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsage.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict1.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict1.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/onlyAliasDeprecated.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/onlyAliasDeprecated.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveFromClass.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveLong.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/typeAliasWithAllGenericParams.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/typeAliasWithAllGenericParams.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt index 3565fafb837..886612c6eea 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt @@ -20,10 +20,7 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.PsiFile import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ConstructorDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.DiagnosticFactory @@ -137,15 +134,19 @@ abstract class DeprecatedSymbolUsageFixBase( return CallableUsageReplacementStrategy(replacement) } - is ClassDescriptor -> { - val replacementType = ReplaceWithAnnotationAnalyzer.analyzeClassReplacement(replaceWith, target, resolutionFacade) - if (replacementType != null) { //TODO: check that it's really resolved and is not an object otherwise it can be expression as well - return ClassUsageReplacementStrategy(replacementType, null, element.project) - } - else { - val constructor = target.unsubstitutedPrimaryConstructor ?: return null - val replacementExpression = ReplaceWithAnnotationAnalyzer.analyzeCallableReplacement(replaceWith, constructor, resolutionFacade) ?: return null - return ClassUsageReplacementStrategy(null, replacementExpression, element.project) + is ClassifierDescriptorWithTypeParameters -> { + val replacementType = ReplaceWithAnnotationAnalyzer.analyzeClassifierReplacement(replaceWith, target, resolutionFacade) + return when { + replacementType != null -> { + //TODO: check that it's really resolved and is not an object otherwise it can be expression as well + ClassUsageReplacementStrategy(replacementType, null, element.project) + } + target is ClassDescriptor -> { + val constructor = target.unsubstitutedPrimaryConstructor ?: return null + val replacementExpression = ReplaceWithAnnotationAnalyzer.analyzeCallableReplacement(replaceWith, constructor, resolutionFacade) ?: return null + ClassUsageReplacementStrategy(null, replacementExpression, element.project) + } + else -> null } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt index d62573097d7..3c0842fa58d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceWithAnnotationAnalyzer.kt @@ -86,9 +86,9 @@ object ReplaceWithAnnotationAnalyzer { .prepareCodeToInline(expression, emptyList(), ::analyzeExpression, importFqNames = importFqNames(annotation)) } - fun analyzeClassReplacement( + fun analyzeClassifierReplacement( annotation: ReplaceWith, - symbolDescriptor: ClassDescriptor, + symbolDescriptor: ClassifierDescriptorWithTypeParameters, resolutionFacade: ResolutionFacade ): KtUserType? { val psiFactory = KtPsiFactory(resolutionFacade.project) @@ -162,16 +162,26 @@ object ReplaceWithAnnotationAnalyzer { getResolutionScope(moduleDescriptor.getPackage(descriptor.fqName), ownerDescriptor, additionalScopes) } - is PackageViewDescriptor -> LexicalScope.Base( - chainImportingScopes(listOf(descriptor.memberScope.memberScopeAsImportingScope()) + additionalScopes)!!, - ownerDescriptor - ) + is PackageViewDescriptor -> { + LexicalScope.Base( + chainImportingScopes(listOf(descriptor.memberScope.memberScopeAsImportingScope()) + additionalScopes)!!, + ownerDescriptor) + } is ClassDescriptor -> { val outerScope = getResolutionScope(descriptor.containingDeclaration, ownerDescriptor, additionalScopes) ?: return null ClassResolutionScopesSupport(descriptor, LockBasedStorageManager.NO_LOCKS, { outerScope }).scopeForMemberDeclarationResolution() } + is TypeAliasDescriptor -> { + val outerScope = getResolutionScope(descriptor.containingDeclaration, ownerDescriptor, additionalScopes) ?: return null + LexicalScopeImpl(outerScope, descriptor, false, null, LexicalScopeKind.TYPE_ALIAS_HEADER, LocalRedeclarationChecker.DO_NOTHING) { + for (typeParameter in descriptor.declaredTypeParameters) { + addClassifierDescriptor(typeParameter) + } + } + } + is FunctionDescriptor -> { val outerScope = getResolutionScope(descriptor.containingDeclaration, ownerDescriptor, additionalScopes) ?: return null FunctionDescriptorUtil.getFunctionInnerScope(outerScope, descriptor, LocalRedeclarationChecker.DO_NOTHING) @@ -186,4 +196,5 @@ object ReplaceWithAnnotationAnalyzer { else -> return null // something local, should not work with ReplaceWith } } + } diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/compoundWithDeprecatedArgumentsAndConstructor.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/compoundWithDeprecatedArgumentsAndConstructor.kt new file mode 100644 index 00000000000..9addf3b7812 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/compoundWithDeprecatedArgumentsAndConstructor.kt @@ -0,0 +1,20 @@ +// "Replace with 'New'" "false" +// ACTION: Convert to block body +// ACTION: Remove explicit type specification + +@Deprecated("Use New", replaceWith = ReplaceWith("New")) +class Old + +@Deprecated("Use New1", replaceWith = ReplaceWith("New1")) +class Old1 + +@Deprecated("Use New2", replaceWith = ReplaceWith("New2")) +class Old2 + +typealias OOO = Old + +class New +class New1 +class New2 + +fun foo(): OOO? = null \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/conflictOnTypeAndAlias.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/conflictOnTypeAndAlias.kt new file mode 100644 index 00000000000..ed4b5fa5b1d --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/conflictOnTypeAndAlias.kt @@ -0,0 +1,17 @@ +// "Replace with 'NewClass'" "false" +// ACTION: Introduce local variable +// ACTION: Replace with 'New' + + +@Deprecated("Use NewClass", ReplaceWith("NewClass")) +class OldClass + +@Deprecated("Use New", ReplaceWith("New")) +typealias Old = OldClass + +class NewClass +typealias New = NewClass + +fun foo() { + Old() +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsage.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsage.kt new file mode 100644 index 00000000000..271043638ea --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsage.kt @@ -0,0 +1,15 @@ +// "Replace with 'New'" "true" +package ppp + +@Deprecated("", ReplaceWith("NewClass")) +class OldClass(p: Int) + +@Deprecated("", ReplaceWith("New")) +typealias Old = OldClass + +class NewClass(p: Int) +typealias New = NewClass + +fun foo() { + Old(1) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsage.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsage.kt.after new file mode 100644 index 00000000000..37132302bd4 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsage.kt.after @@ -0,0 +1,15 @@ +// "Replace with 'New'" "true" +package ppp + +@Deprecated("", ReplaceWith("NewClass")) +class OldClass(p: Int) + +@Deprecated("", ReplaceWith("New")) +typealias Old = OldClass + +class NewClass(p: Int) +typealias New = NewClass + +fun foo() { + New(1) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict.kt new file mode 100644 index 00000000000..fa9006d7c0b --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict.kt @@ -0,0 +1,16 @@ +// "Replace with 'New'" "false" +// ACTION: Convert to block body +// ACTION: Introduce local variable +// ACTION: Replace with 'NewClass(12)' + +@Deprecated("Use NewClass", replaceWith = ReplaceWith("NewClass")) +class OldClass @Deprecated("Use NewClass(12)", replaceWith = ReplaceWith("NewClass(12)")) constructor() + +@Deprecated("Use New", replaceWith = ReplaceWith("New")) +typealias Old = OldClass + +class NewClass(p: Int = 12) +typealias New = NewClass + +// 'New' replacement shouldn't be used because it may brake code behaviour +fun foo() = Old() \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict1.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict1.kt new file mode 100644 index 00000000000..d697cac310c --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict1.kt @@ -0,0 +1,12 @@ +// "Replace with 'NewClass(12)'" "true" + +@Deprecated("Use NewClass", replaceWith = ReplaceWith("NewClass")) +class OldClass @Deprecated("Use NewClass(12)", replaceWith = ReplaceWith("NewClass(12)")) constructor() + +@Deprecated("Use New", replaceWith = ReplaceWith("New")) +typealias Old = OldClass + +class NewClass(p: Int = 12) +typealias New = NewClass + +fun foo() = Old() \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict1.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict1.kt.after new file mode 100644 index 00000000000..900478199b5 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict1.kt.after @@ -0,0 +1,12 @@ +// "Replace with 'NewClass(12)'" "true" + +@Deprecated("Use NewClass", replaceWith = ReplaceWith("NewClass")) +class OldClass @Deprecated("Use NewClass(12)", replaceWith = ReplaceWith("NewClass(12)")) constructor() + +@Deprecated("Use New", replaceWith = ReplaceWith("New")) +typealias Old = OldClass + +class NewClass(p: Int = 12) +typealias New = NewClass + +fun foo() = NewClass(12) \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/onlyAliasDeprecated.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/onlyAliasDeprecated.kt new file mode 100644 index 00000000000..61cb4b6892c --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/onlyAliasDeprecated.kt @@ -0,0 +1,8 @@ +// "Replace with 'Some'" "true" + +class Some + +@Deprecated("Use Some instead", replaceWith = ReplaceWith("Some")) +typealias A = Some + +val a: A = A() \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/onlyAliasDeprecated.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/onlyAliasDeprecated.kt.after new file mode 100644 index 00000000000..b39db11c642 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/onlyAliasDeprecated.kt.after @@ -0,0 +1,8 @@ +// "Replace with 'Some'" "true" + +class Some + +@Deprecated("Use Some instead", replaceWith = ReplaceWith("Some")) +typealias A = Some + +val a: Some = A() \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveFromClass.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveFromClass.kt new file mode 100644 index 00000000000..bdac8b7da4d --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveFromClass.kt @@ -0,0 +1,12 @@ +// "Replace with 'NewClass'" "false" +// ACTION: Convert to block body +// ACTION: Remove explicit type specification + + +@Deprecated("", ReplaceWith("NewClass")) +class OldClass +typealias Old = OldClass + +class NewClass + +fun foo(): Old = null!! \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveLong.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveLong.kt new file mode 100644 index 00000000000..6f2bfc73b47 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveLong.kt @@ -0,0 +1,14 @@ +// "Replace with 'NewClass'" "false" +// ACTION: Convert to block body +// ACTION: Introduce local variable + + +@Deprecated("", replaceWith = ReplaceWith("NewClass")) +class OldClass() + +typealias Old1 = OldClass +typealias Old2 = Old1 + +class NewClass() + +fun foo() = Old2() \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/typeAliasWithAllGenericParams.kt b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/typeAliasWithAllGenericParams.kt new file mode 100644 index 00000000000..74c2353be8e --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/typeAliasWithAllGenericParams.kt @@ -0,0 +1,10 @@ +// "Replace with 'X'" "true" + +package ppp + +class X + +@Deprecated("Will be dropped", replaceWith = ReplaceWith("X", "ppp.X")) +typealias IntX = X + +fun foo(ix: IntX) {} \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/typeAliasWithAllGenericParams.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/typeAliasWithAllGenericParams.kt.after new file mode 100644 index 00000000000..84a0f5ffbef --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/typeAliasWithAllGenericParams.kt.after @@ -0,0 +1,10 @@ +// "Replace with 'X'" "true" + +package ppp + +class X + +@Deprecated("Will be dropped", replaceWith = ReplaceWith("X", "ppp.X")) +typealias IntX = X + +fun foo(ix: X) {} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 9363b0412a0..f61959b170f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -5405,6 +5405,69 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeAliases") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class TypeAliases extends AbstractQuickFixTest { + public void testAllFilesPresentInTypeAliases() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/typeAliases"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("compoundWithDeprecatedArgumentsAndConstructor.kt") + public void testCompoundWithDeprecatedArgumentsAndConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/compoundWithDeprecatedArgumentsAndConstructor.kt"); + doTest(fileName); + } + + @TestMetadata("conflictOnTypeAndAlias.kt") + public void testConflictOnTypeAndAlias() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/conflictOnTypeAndAlias.kt"); + doTest(fileName); + } + + @TestMetadata("constructorUsage.kt") + public void testConstructorUsage() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsage.kt"); + doTest(fileName); + } + + @TestMetadata("constructorUsageWithConflict.kt") + public void testConstructorUsageWithConflict() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict.kt"); + doTest(fileName); + } + + @TestMetadata("constructorUsageWithConflict1.kt") + public void testConstructorUsageWithConflict1() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/constructorUsageWithConflict1.kt"); + doTest(fileName); + } + + @TestMetadata("onlyAliasDeprecated.kt") + public void testOnlyAliasDeprecated() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/onlyAliasDeprecated.kt"); + doTest(fileName); + } + + @TestMetadata("transitiveFromClass.kt") + public void testTransitiveFromClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveFromClass.kt"); + doTest(fileName); + } + + @TestMetadata("transitiveLong.kt") + public void testTransitiveLong() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/transitiveLong.kt"); + doTest(fileName); + } + + @TestMetadata("typeAliasWithAllGenericParams.kt") + public void testTypeAliasWithAllGenericParams() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeAliases/typeAliasWithAllGenericParams.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeArguments") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)