From e31ea0c243bf70333d5533316ca27e678c4e623b Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Mon, 15 Jun 2020 11:02:56 +0900 Subject: [PATCH] ReplaceWith: suggest to replace for 'get/set' operator functions #KT-15944 Fixed --- .../CallableUsageReplacementStrategy.kt | 31 ++++++++++++------- .../ClassUsageReplacementStrategy.kt | 2 +- .../PropertyUsageReplacementStrategy.kt | 4 +-- .../codeInliner/UsageReplacementStrategy.kt | 12 +++---- .../replaceWith/DeprecatedSymbolUsageFix.kt | 8 ++--- .../DeprecatedSymbolUsageFixBase.kt | 21 +++++++------ .../DeprecatedSymbolUsageInWholeProjectFix.kt | 6 ++-- .../operatorCalls/get.kt | 11 +++++++ .../operatorCalls/get.kt.after | 11 +++++++ .../operatorCalls/set.kt | 12 +++++++ .../operatorCalls/set.kt.after | 12 +++++++ .../idea/quickfix/QuickFixTestGenerated.java | 10 ++++++ 12 files changed, 103 insertions(+), 37 deletions(-) create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/get.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/get.kt.after create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/set.kt create mode 100644 idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/set.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CallableUsageReplacementStrategy.kt b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CallableUsageReplacementStrategy.kt index b15d6d333d4..97cd3e2e2d0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInliner/CallableUsageReplacementStrategy.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInliner/CallableUsageReplacementStrategy.kt @@ -19,10 +19,7 @@ package org.jetbrains.kotlin.idea.codeInliner import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtOperationReferenceExpression -import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode @@ -31,7 +28,7 @@ class CallableUsageReplacementStrategy( private val replacement: CodeToInline, private val inlineSetter: Boolean = false ) : UsageReplacementStrategy { - override fun createReplacer(usage: KtSimpleNameExpression): (() -> KtElement?)? { + override fun createReplacer(usage: KtReferenceExpression): (() -> KtElement?)? { val bindingContext = usage.analyze(BodyResolveMode.PARTIAL_WITH_CFA) val resolvedCall = usage.getResolvedCall(bindingContext) ?: return null if (!resolvedCall.status.isSuccess) return null @@ -45,13 +42,25 @@ class CallableUsageReplacementStrategy( //TODO: precheck pattern correctness for annotation entry - return { - if (usage is KtOperationReferenceExpression && usage.getReferencedNameElementType() != KtTokens.IDENTIFIER) { - val nameExpression = OperatorToFunctionIntention.convert(usage.parent as KtExpression).second - createReplacer(nameExpression)?.invoke() - } else { - CodeInliner(usage, bindingContext, resolvedCall, callElement, inlineSetter, replacement).doInline() + return when { + usage is KtArrayAccessExpression -> { + { + val nameExpression = OperatorToFunctionIntention.convert(usage).second + createReplacer(nameExpression)?.invoke() + } } + usage is KtOperationReferenceExpression && usage.getReferencedNameElementType() != KtTokens.IDENTIFIER -> { + { + val nameExpression = OperatorToFunctionIntention.convert(usage.parent as KtExpression).second + createReplacer(nameExpression)?.invoke() + } + } + usage is KtSimpleNameExpression -> { + { + CodeInliner(usage, bindingContext, resolvedCall, callElement, inlineSetter, replacement).doInline() + } + } + else -> null } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInliner/ClassUsageReplacementStrategy.kt b/idea/src/org/jetbrains/kotlin/idea/codeInliner/ClassUsageReplacementStrategy.kt index 35be8099dd1..421df49f015 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInliner/ClassUsageReplacementStrategy.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInliner/ClassUsageReplacementStrategy.kt @@ -45,7 +45,7 @@ class ClassUsageReplacementStrategy( CallableUsageReplacementStrategy(it, inlineSetter = false) } - override fun createReplacer(usage: KtSimpleNameExpression): (() -> KtElement?)? { + override fun createReplacer(usage: KtReferenceExpression): (() -> KtElement?)? { if (usage !is KtNameReferenceExpression) return null constructorReplacementStrategy?.createReplacer(usage)?.let { return it } diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInliner/PropertyUsageReplacementStrategy.kt b/idea/src/org/jetbrains/kotlin/idea/codeInliner/PropertyUsageReplacementStrategy.kt index 00695389a31..a6ab3e191c7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInliner/PropertyUsageReplacementStrategy.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInliner/PropertyUsageReplacementStrategy.kt @@ -19,7 +19,7 @@ package org.jetbrains.kotlin.idea.codeInliner import org.jetbrains.kotlin.idea.references.ReferenceAccess import org.jetbrains.kotlin.idea.references.readWriteAccess import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.KtReferenceExpression class PropertyUsageReplacementStrategy(readReplacement: CodeToInline?, writeReplacement: CodeToInline?) : UsageReplacementStrategy { private val readReplacementStrategy = readReplacement?.let { @@ -29,7 +29,7 @@ class PropertyUsageReplacementStrategy(readReplacement: CodeToInline?, writeRepl CallableUsageReplacementStrategy(it, inlineSetter = true) } - override fun createReplacer(usage: KtSimpleNameExpression): (() -> KtElement?)? { + override fun createReplacer(usage: KtReferenceExpression): (() -> KtElement?)? { val access = usage.readWriteAccess(useResolveForReadWrite = true) return when (access) { ReferenceAccess.READ -> readReplacementStrategy?.createReplacer(usage) diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInliner/UsageReplacementStrategy.kt b/idea/src/org/jetbrains/kotlin/idea/codeInliner/UsageReplacementStrategy.kt index bad7fc68451..b8bddf32dd6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInliner/UsageReplacementStrategy.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInliner/UsageReplacementStrategy.kt @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.core.targetDescriptors import org.jetbrains.kotlin.idea.intentions.ConvertReferenceToLambdaIntention import org.jetbrains.kotlin.idea.intentions.SpecifyExplicitLambdaSignatureIntention -import org.jetbrains.kotlin.idea.references.KtSimpleNameReference +import org.jetbrains.kotlin.idea.references.KtSimpleReference import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope import org.jetbrains.kotlin.idea.util.application.executeWriteCommand @@ -35,7 +35,7 @@ import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType interface UsageReplacementStrategy { - fun createReplacer(usage: KtSimpleNameExpression): (() -> KtElement?)? + fun createReplacer(usage: KtReferenceExpression): (() -> KtElement?)? } private val LOG = Logger.getInstance(UsageReplacementStrategy::class.java) @@ -53,7 +53,7 @@ fun UsageReplacementStrategy.replaceUsagesInWholeProject( val usages = runReadAction { val searchScope = KotlinSourceFilterScope.projectSources(GlobalSearchScope.projectScope(project), project) ReferencesSearch.search(targetPsiElement, searchScope) - .filterIsInstance() + .filterIsInstance>() .map { ref -> ref.expression } } this@replaceUsagesInWholeProject.replaceUsages(usages, targetPsiElement, project, commandName, postAction) @@ -62,7 +62,7 @@ fun UsageReplacementStrategy.replaceUsagesInWholeProject( } fun UsageReplacementStrategy.replaceUsages( - usages: Collection, + usages: Collection, targetPsiElement: PsiElement, project: Project, commandName: String, @@ -104,7 +104,7 @@ fun UsageReplacementStrategy.replaceUsages( * @return false if some usages were invalidated */ private fun UsageReplacementStrategy.processUsages( - usages: List, + usages: List, targetDeclaration: KtNamedDeclaration?, importsToDelete: MutableList ): Boolean { @@ -116,7 +116,7 @@ private fun UsageReplacementStrategy.processUsages( continue } - if (specialUsageProcessing(usage, targetDeclaration)) continue + if (usage is KtSimpleNameExpression && specialUsageProcessing(usage, targetDeclaration)) continue //TODO: keep the import if we don't know how to replace some of the usages val importDirective = usage.getStrictParentOfType() diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFix.kt index ebe372c1be6..be910e77c62 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFix.kt @@ -28,11 +28,11 @@ import org.jetbrains.kotlin.idea.core.targetDescriptors import org.jetbrains.kotlin.idea.quickfix.CleanupFix import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory import org.jetbrains.kotlin.psi.KtImportDirective -import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.KtReferenceExpression import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny class DeprecatedSymbolUsageFix( - element: KtSimpleNameExpression, + element: KtReferenceExpression, replaceWith: ReplaceWith ) : DeprecatedSymbolUsageFixBase(element, replaceWith), CleanupFix, HighPriorityAction { @@ -51,8 +51,8 @@ class DeprecatedSymbolUsageFix( companion object : KotlinSingleIntentionActionFactory() { override fun createAction(diagnostic: Diagnostic): IntentionAction? { - val (nameExpression, replacement) = extractDataFromDiagnostic(diagnostic, false) ?: return null - return DeprecatedSymbolUsageFix(nameExpression, replacement) + val (referenceExpression, replacement) = extractDataFromDiagnostic(diagnostic, false) ?: return null + return DeprecatedSymbolUsageFix(referenceExpression, replacement) } fun isImportToBeRemoved(import: KtImportDirective): Boolean { 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 e79b5f4e113..454731036b3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageFixBase.kt @@ -52,9 +52,9 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs //TODO: different replacements for property accessors abstract class DeprecatedSymbolUsageFixBase( - element: KtSimpleNameExpression, + element: KtReferenceExpression, val replaceWith: ReplaceWith -) : KotlinQuickFixAction(element) { +) : KotlinQuickFixAction(element) { override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean { val element = element ?: return false @@ -76,7 +76,7 @@ abstract class DeprecatedSymbolUsageFixBase( fun fetchReplaceWithPattern( descriptor: DeclarationDescriptor, project: Project, - contextElement: KtSimpleNameExpression?, + contextElement: KtReferenceExpression?, replaceInWholeProject: Boolean ): ReplaceWith? { val annotation = descriptor.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.deprecated) ?: return null @@ -102,7 +102,7 @@ abstract class DeprecatedSymbolUsageFixBase( } private fun String.applyContextElement( - element: KtSimpleNameExpression?, + element: KtReferenceExpression?, descriptor: DeclarationDescriptor ): String { if (element == null) return this @@ -155,13 +155,14 @@ abstract class DeprecatedSymbolUsageFixBase( } data class Data( - val nameExpression: KtSimpleNameExpression, + val referenceExpression: KtReferenceExpression, val replaceWith: ReplaceWith, val descriptor: DeclarationDescriptor ) fun extractDataFromDiagnostic(deprecatedDiagnostic: Diagnostic, replaceInWholeProject: Boolean): Data? { - val nameExpression: KtSimpleNameExpression = when (val psiElement = deprecatedDiagnostic.psiElement) { + val referenceExpression = when (val psiElement = deprecatedDiagnostic.psiElement) { + is KtArrayAccessExpression -> psiElement is KtSimpleNameExpression -> psiElement is KtConstructorCalleeExpression -> psiElement.constructorReferenceExpression else -> null @@ -178,12 +179,12 @@ abstract class DeprecatedSymbolUsageFixBase( } val replacement = - fetchReplaceWithPattern(descriptor, nameExpression.project, nameExpression, replaceInWholeProject) ?: return null - return Data(nameExpression, replacement, descriptor) + fetchReplaceWithPattern(descriptor, referenceExpression.project, referenceExpression, replaceInWholeProject) ?: return null + return Data(referenceExpression, replacement, descriptor) } private fun buildUsageReplacementStrategy( - element: KtSimpleNameExpression, + element: KtReferenceExpression, replaceWith: ReplaceWith, recheckAnnotation: Boolean, reformat: Boolean, @@ -269,7 +270,7 @@ abstract class DeprecatedSymbolUsageFixBase( project: Project, classifier: ClassifierDescriptorWithTypeParameters, typeAlias: PsiElement, - contextElement: KtSimpleNameExpression, + contextElement: KtReferenceExpression, replaceInWholeProject: Boolean ): ConstructorDescriptor? { val specialReplaceWithForConstructor = classifier.constructors.filter { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageInWholeProjectFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageInWholeProjectFix.kt index 8143dd5db55..3e5f2644fe1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageInWholeProjectFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/DeprecatedSymbolUsageInWholeProjectFix.kt @@ -20,7 +20,7 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy class DeprecatedSymbolUsageInWholeProjectFix( - element: KtSimpleNameExpression, + element: KtReferenceExpression, replaceWith: ReplaceWith, private val text: String ) : DeprecatedSymbolUsageFixBase(element, replaceWith) { @@ -68,10 +68,10 @@ class DeprecatedSymbolUsageInWholeProjectFix( } override fun createAction(diagnostic: Diagnostic): IntentionAction? { - val (nameExpression, replacement, descriptor) = extractDataFromDiagnostic(diagnostic, true) ?: return null + val (referenceExpression, replacement, descriptor) = extractDataFromDiagnostic(diagnostic, true) ?: return null val descriptorName = RENDERER.render(descriptor) return DeprecatedSymbolUsageInWholeProjectFix( - nameExpression, + referenceExpression, replacement, KotlinBundle.message("replace.usages.of.0.in.whole.project", descriptorName) ) diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/get.kt b/idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/get.kt new file mode 100644 index 00000000000..76ab636884b --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/get.kt @@ -0,0 +1,11 @@ +// "Replace with 'get2(i)'" "true" +interface T { + @Deprecated("", replaceWith = ReplaceWith("get2(i)")) + operator fun get(i: Int): String + + fun get2(i: Int): String +} + +fun test(t: T) { + val s = t[0] +} \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/get.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/get.kt.after new file mode 100644 index 00000000000..fee6556ecdc --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/get.kt.after @@ -0,0 +1,11 @@ +// "Replace with 'get2(i)'" "true" +interface T { + @Deprecated("", replaceWith = ReplaceWith("get2(i)")) + operator fun get(i: Int): String + + fun get2(i: Int): String +} + +fun test(t: T) { + val s = t.get2(0) +} \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/set.kt b/idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/set.kt new file mode 100644 index 00000000000..1e541e5071b --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/set.kt @@ -0,0 +1,12 @@ +// "Replace usages of 'set(Int, Int): Unit' in whole project" "true" +interface T { + @Deprecated("", replaceWith = ReplaceWith("set2(i, v)")) + operator fun set(i: Int, v: Int) + + fun set2(i: Int, v: Int) +} + +fun test(t: T) { + t[0] = 1 + t[1] = 2 +} \ No newline at end of file diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/set.kt.after b/idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/set.kt.after new file mode 100644 index 00000000000..5e75dfaef07 --- /dev/null +++ b/idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/set.kt.after @@ -0,0 +1,12 @@ +// "Replace usages of 'set(Int, Int): Unit' in whole project" "true" +interface T { + @Deprecated("", replaceWith = ReplaceWith("set2(i, v)")) + operator fun set(i: Int, v: Int) + + fun set2(i: Int, v: Int) +} + +fun test(t: T) { + t.set2(0, 1) + t.set2(1, 2) +} \ 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 9916740657d..5885dc71fe1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -6991,6 +6991,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), null, true); } + @TestMetadata("get.kt") + public void testGet() throws Exception { + runTest("idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/get.kt"); + } + @TestMetadata("in.kt") public void testIn() throws Exception { runTest("idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/in.kt"); @@ -7000,6 +7005,11 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { public void testPlusAssign() throws Exception { runTest("idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/plusAssign.kt"); } + + @TestMetadata("set.kt") + public void testSet() throws Exception { + runTest("idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/set.kt"); + } } @TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters")