diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ClassUsageReplacementStrategy.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ClassUsageReplacementStrategy.kt index c94f3e65c77..40e8a854a93 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ClassUsageReplacementStrategy.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ClassUsageReplacementStrategy.kt @@ -18,17 +18,19 @@ package org.jetbrains.kotlin.idea.quickfix.replaceWith import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.util.ShortenReferences -import org.jetbrains.kotlin.psi.JetElement -import org.jetbrains.kotlin.psi.JetNameReferenceExpression -import org.jetbrains.kotlin.psi.JetSimpleNameExpression -import org.jetbrains.kotlin.psi.JetUserType +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis class ClassUsageReplacementStrategy( private val replacement: JetUserType ) : UsageReplacementStrategy { + private val factory = JetPsiFactory(replacement) + private val qualifierAsExpression = replacement.qualifier?.let { factory.createExpression(it.text) } + override fun createReplacer(usage: JetSimpleNameExpression): (() -> JetElement)? { if (usage !is JetNameReferenceExpression) return null + if (replacement.referenceExpression == null) return null val parent = usage.parent when (parent) { @@ -39,7 +41,31 @@ class ClassUsageReplacementStrategy( } //TODO: type arguments and type arguments of outer class are lost } + is JetCallExpression -> { + if (usage != parent.calleeExpression) return null + return { replaceConstructorCall(parent) } + } + else -> return null //TODO } } + + private fun replaceConstructorCall(callExpression: JetCallExpression): JetElement { + callExpression.calleeExpression!!.replace(replacement.referenceExpression!!) + + val expressionToReplace = callExpression.getQualifiedExpressionForSelectorOrThis() + val newExpression = if (qualifierAsExpression != null) + factory.createExpressionByPattern("$0.$1", qualifierAsExpression, callExpression) + else + callExpression + + val result = if (expressionToReplace != newExpression) { + expressionToReplace.replaced(newExpression) + } + else { + expressionToReplace + } + + return ShortenReferences.DEFAULT.process(result) + } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/UsageReplacementStrategy.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/UsageReplacementStrategy.kt index 55852cb4899..c354cd57a7f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/UsageReplacementStrategy.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/UsageReplacementStrategy.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.quickfix.replaceWith import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.psi.JetElement @@ -32,10 +33,16 @@ interface UsageReplacementStrategy { fun build(element: JetSimpleNameExpression, replaceWith: ReplaceWith): UsageReplacementStrategy? { val resolutionFacade = element.getResolutionFacade() val bindingContext = resolutionFacade.analyze(element, BodyResolveMode.PARTIAL) - val target = element.mainReference.resolveToDescriptors(bindingContext).singleOrNull() ?: return null + var target = element.mainReference.resolveToDescriptors(bindingContext).singleOrNull() ?: return null + + var replacePatternFromSymbol = DeprecatedSymbolUsageFixBase.replaceWithPattern(target, resolutionFacade.project) + if (replacePatternFromSymbol == null && target is ConstructorDescriptor) { + target = target.containingDeclaration + replacePatternFromSymbol = DeprecatedSymbolUsageFixBase.replaceWithPattern(target, resolutionFacade.project) + } // check that ReplaceWith hasn't changed - if (DeprecatedSymbolUsageFixBase.replaceWithPattern(target, resolutionFacade.project) != replaceWith) return null + if (replacePatternFromSymbol != replaceWith) return null when (target) { is CallableDescriptor -> { diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage1.kt b/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage1.kt new file mode 100644 index 00000000000..31904935931 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage1.kt @@ -0,0 +1,10 @@ +// "Replace with 'NewClass'" "true" + +@deprecated("", ReplaceWith("NewClass")) +class OldClass(p: Int) + +class NewClass(p: Int) + +fun foo() { + OldClass(1) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage1.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage1.kt.after new file mode 100644 index 00000000000..54a5bd5a6f0 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage1.kt.after @@ -0,0 +1,10 @@ +// "Replace with 'NewClass'" "true" + +@deprecated("", ReplaceWith("NewClass")) +class OldClass(p: Int) + +class NewClass(p: Int) + +fun foo() { + NewClass(1) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage2.kt b/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage2.kt new file mode 100644 index 00000000000..5d65a592e5c --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage2.kt @@ -0,0 +1,8 @@ +// "Replace with 'java.util.Random'" "true" + +@deprecated("", ReplaceWith("java.util.Random")) +class OldClass + +fun foo() { + OldClass(1) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage2.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage2.kt.after new file mode 100644 index 00000000000..2d8df65832a --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage2.kt.after @@ -0,0 +1,10 @@ +import java.util.Random + +// "Replace with 'java.util.Random'" "true" + +@deprecated("", ReplaceWith("java.util.Random")) +class OldClass + +fun foo() { + Random(1) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage3.kt b/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage3.kt new file mode 100644 index 00000000000..29d92f7d6c9 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage3.kt @@ -0,0 +1,11 @@ +// "Replace with 'NewClass'" "true" +package ppp + +@deprecated("", ReplaceWith("NewClass")) +class OldClass(p: Int) + +class NewClass(p: Int) + +fun foo() { + ppp.OldClass(1) +} diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage3.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage3.kt.after new file mode 100644 index 00000000000..4bae9433816 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage3.kt.after @@ -0,0 +1,11 @@ +// "Replace with 'NewClass'" "true" +package ppp + +@deprecated("", ReplaceWith("NewClass")) +class OldClass(p: Int) + +class NewClass(p: Int) + +fun foo() { + NewClass(1) +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index b0dd69af55a..f25d1a3dfa5 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -3312,6 +3312,24 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/classUsages"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); } + @TestMetadata("constructorUsage1.kt") + public void testConstructorUsage1() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage1.kt"); + doTest(fileName); + } + + @TestMetadata("constructorUsage2.kt") + public void testConstructorUsage2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage2.kt"); + doTest(fileName); + } + + @TestMetadata("constructorUsage3.kt") + public void testConstructorUsage3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage3.kt"); + doTest(fileName); + } + @TestMetadata("imports.kt") public void testImports() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/imports.kt");