Enable ReplaceWith for type aliases (KT-14929)

#KT-14929 Fixed
This commit is contained in:
Nikolay Krasko
2017-07-21 19:55:45 +03:00
parent 224df7a1ea
commit 5df2698f77
16 changed files with 263 additions and 19 deletions
@@ -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
}
}
@@ -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
}
}
}
@@ -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
@@ -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)
}
@@ -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)
}
@@ -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()
@@ -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()
@@ -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)
@@ -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()
@@ -0,0 +1,8 @@
// "Replace with 'Some'" "true"
class Some
@Deprecated("Use Some instead", replaceWith = ReplaceWith("Some"))
typealias A = Some
val a: Some = A()
@@ -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()
@@ -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) {}
@@ -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)