Enable ReplaceWith for type aliases (KT-14929)
#KT-14929 Fixed
This commit is contained in:
+14
-13
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-6
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// "Replace with 'New<T, U>'" "false"
|
||||
// ACTION: Convert to block body
|
||||
// ACTION: Remove explicit type specification
|
||||
|
||||
@Deprecated("Use New", replaceWith = ReplaceWith("New<T, U>"))
|
||||
class Old<T, U>
|
||||
|
||||
@Deprecated("Use New1", replaceWith = ReplaceWith("New1"))
|
||||
class Old1
|
||||
|
||||
@Deprecated("Use New2", replaceWith = ReplaceWith("New2"))
|
||||
class Old2
|
||||
|
||||
typealias OOO = Old<Old1, Old2>
|
||||
|
||||
class New<T, U>
|
||||
class New1
|
||||
class New2
|
||||
|
||||
fun foo(): <caret>OOO? = null
|
||||
+17
@@ -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() {
|
||||
<caret>Old()
|
||||
}
|
||||
@@ -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() {
|
||||
<caret>Old(1)
|
||||
}
|
||||
+15
@@ -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)
|
||||
}
|
||||
+16
@@ -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() = <caret>Old()
|
||||
+12
@@ -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() = <caret>Old()
|
||||
Vendored
+12
@@ -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)
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Replace with 'Some'" "true"
|
||||
|
||||
class Some
|
||||
|
||||
@Deprecated("Use Some instead", replaceWith = ReplaceWith("Some"))
|
||||
typealias A = Some
|
||||
|
||||
val a: <caret>A = A()
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Replace with 'Some'" "true"
|
||||
|
||||
class Some
|
||||
|
||||
@Deprecated("Use Some instead", replaceWith = ReplaceWith("Some"))
|
||||
typealias A = Some
|
||||
|
||||
val a: Some = A()
|
||||
+12
@@ -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(): <caret>Old = null!!
|
||||
@@ -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() = <caret>Old2()
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace with 'X<Int>'" "true"
|
||||
|
||||
package ppp
|
||||
|
||||
class X<T>
|
||||
|
||||
@Deprecated("Will be dropped", replaceWith = ReplaceWith("X<Int>", "ppp.X"))
|
||||
typealias IntX = X<Int>
|
||||
|
||||
fun foo(ix: <caret>IntX) {}
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace with 'X<Int>'" "true"
|
||||
|
||||
package ppp
|
||||
|
||||
class X<T>
|
||||
|
||||
@Deprecated("Will be dropped", replaceWith = ReplaceWith("X<Int>", "ppp.X"))
|
||||
typealias IntX = X<Int>
|
||||
|
||||
fun foo(ix: <caret>X<Int>) {}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user