From 86a30d35a5eeff962aefa6565f1c4e4e24639acf Mon Sep 17 00:00:00 2001 From: Bart van Helvert Date: Fri, 27 Oct 2023 14:56:12 +0200 Subject: [PATCH] [jvm] Clean up `CallChainReplacementInfo` API GitOrigin-RevId: 209a492001b6a7872b2ed1b57da57daa3e7ee184 --- .../ReplaceCallableExpressionQuickFix.kt | 5 +- .../ReplaceCallableExpressionUtil.kt | 73 ++++++++++--------- .../testFramework/JvmInspectionTestBase.kt | 4 +- 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/jvm/jvm-analysis-quickFix/src/com/intellij/jvm/analysis/quickFix/ReplaceCallableExpressionQuickFix.kt b/jvm/jvm-analysis-quickFix/src/com/intellij/jvm/analysis/quickFix/ReplaceCallableExpressionQuickFix.kt index e23b7e5a8317..b7b3a4d8f1b7 100644 --- a/jvm/jvm-analysis-quickFix/src/com/intellij/jvm/analysis/quickFix/ReplaceCallableExpressionQuickFix.kt +++ b/jvm/jvm-analysis-quickFix/src/com/intellij/jvm/analysis/quickFix/ReplaceCallableExpressionQuickFix.kt @@ -1,7 +1,6 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.jvm.analysis.quickFix -import com.intellij.codeInsight.intention.FileModifier.SafeFieldForPreview import com.intellij.codeInspection.CommonQuickFixBundle import com.intellij.jvm.analysis.refactoring.CallChainReplacementInfo import com.intellij.jvm.analysis.refactoring.replaceWithCallChain @@ -12,8 +11,8 @@ import com.intellij.psi.PsiElement import org.jetbrains.uast.UCallExpression import org.jetbrains.uast.getUastParentOfType -class ReplaceCallableExpressionQuickFix(@SafeFieldForPreview private val callChainReplacementInfo: CallChainReplacementInfo) : PsiUpdateModCommandQuickFix() { - override fun getFamilyName(): String = CommonQuickFixBundle.message("fix.replace.with.x", callChainReplacementInfo.presentation()) +class ReplaceCallableExpressionQuickFix(private val callChainReplacementInfo: CallChainReplacementInfo) : PsiUpdateModCommandQuickFix() { + override fun getFamilyName(): String = CommonQuickFixBundle.message("fix.replace.with.x", callChainReplacementInfo.presentation) override fun applyFix(project: Project, element: PsiElement, updater: ModPsiUpdater) { val uCall = element.getUastParentOfType() ?: return diff --git a/jvm/jvm-analysis-refactoring/src/com/intellij/jvm/analysis/refactoring/ReplaceCallableExpressionUtil.kt b/jvm/jvm-analysis-refactoring/src/com/intellij/jvm/analysis/refactoring/ReplaceCallableExpressionUtil.kt index 2af2bda93ed7..cbfc9b570216 100644 --- a/jvm/jvm-analysis-refactoring/src/com/intellij/jvm/analysis/refactoring/ReplaceCallableExpressionUtil.kt +++ b/jvm/jvm-analysis-refactoring/src/com/intellij/jvm/analysis/refactoring/ReplaceCallableExpressionUtil.kt @@ -1,45 +1,50 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.jvm.analysis.refactoring +import com.intellij.codeInsight.intention.FileModifier.SafeTypeForPreview +import com.intellij.psi.PsiElement import com.intellij.psi.PsiType +import com.intellij.psi.SmartPsiElementPointer import com.intellij.util.concurrency.annotations.RequiresWriteLock import org.jetbrains.uast.* import org.jetbrains.uast.generate.getUastElementFactory import org.jetbrains.uast.generate.shortenReference /** - * Class represents info about the reference to the class and chain of methods, which will replace the old link - * @param qualifiedReference defines the FQN for the reference. If null, methods called without receiver - * @param callReplacementInfos defines information about methods, + * A call chains that can be used for a replacement by [replaceWithCallChain] + * + * Example: + * ``` + * org.jetbrains.Foo.bar().fooBar() + * ``` + * Here `org.jetbrains.Foo` is the [qualifiedReference] and `bar()` and `fooBar()` are the [callReplacementInfos]. + * + * @param qualifiedReference the receiver of the call chain, null for methods without a receiver + * @param callReplacementInfos the call chain */ -class CallChainReplacementInfo(val qualifiedReference: String?, - vararg val callReplacementInfos: CallReplacementInfo) { - fun presentation(): String = if (qualifiedReference == null) { - callReplacementInfos.joinToString(separator = "().", postfix = "()") { it.name } - } - else if (callReplacementInfos.isEmpty()) { - qualifiedReference - } - else { - "$qualifiedReference.${callReplacementInfos.joinToString(separator = "().", postfix = "()") { it.name }}" - } +@SafeTypeForPreview +class CallChainReplacementInfo(val qualifiedReference: String?, vararg val callReplacementInfos: CallReplacementInfo) { + private val qualifierPresentation get() = if (qualifiedReference != null) { + "$qualifiedReference" + if (callReplacementInfos.isNotEmpty()) "." else "" + } else "" + + private val callPresentation get() = if (callReplacementInfos.isNotEmpty()) { + callReplacementInfos.joinToString(separator = "().", postfix = "()", transform = CallReplacementInfo::name) + } else "" + + val presentation: String get() = qualifierPresentation + callPresentation } /** - * Class represents info about the method, which will be a part of replacement reference procedure - * @see CallChainReplacementInfo - * @param name defines FQN name of the method - * @param returnType defines method's return type. If null, then we consider, that method returns Void + * Call info that can be used for a replacement used by [CallChainReplacementInfo]. */ -class CallReplacementInfo(val name: String, - val returnType: PsiType? = null, - vararg parameters: UExpression) { - val parametersPointers = parameters.map { it.toSmartPsiElementPointer() } +@SafeTypeForPreview +class CallReplacementInfo(val name: String, val returnType: PsiType? = null, vararg parameters: UExpression) { + val parametersPointers: List> = parameters.mapNotNull(UExpression::toSmartPsiElementPointer) } /** - * @see CallChainReplacementInfo and - * @see CallReplacementInfo + * Replaces a [UCallExpression] with a call as described by [CallChainReplacementInfo]. */ @RequiresWriteLock fun UCallExpression.replaceWithCallChain(callChainReplacementInfo: CallChainReplacementInfo) { @@ -52,17 +57,15 @@ fun UCallExpression.replaceWithCallChain(callChainReplacementInfo: CallChainRepl } ?: receiver val newUElement = callChainReplacementInfo.callReplacementInfos.fold(uQualifiedReference) { receiver, method -> - val uExpressions = method.parametersPointers.mapNotNull { it?.element.toUElementOfType() } - - if (uExpressions.size != method.parametersPointers.size) { - return@replaceWithCallChain - } - - uFactory?.createCallExpression(receiver?.getQualifiedParentOrThis(), - method.name, - uExpressions, - method.returnType, - UastCallKind.METHOD_CALL) + val uExpressions = method.parametersPointers.mapNotNull { it.element.toUElementOfType() } + if (uExpressions.size != method.parametersPointers.size) return@replaceWithCallChain + uFactory?.createCallExpression( + receiver?.getQualifiedParentOrThis(), + method.name, + uExpressions, + method.returnType, + UastCallKind.METHOD_CALL + ) } val oldPsi = oldUParent.sourcePsi ?: return diff --git a/jvm/jvm-analysis-testFramework/src/com/intellij/jvm/analysis/testFramework/JvmInspectionTestBase.kt b/jvm/jvm-analysis-testFramework/src/com/intellij/jvm/analysis/testFramework/JvmInspectionTestBase.kt index a9c6524e795e..877d98ac0c64 100644 --- a/jvm/jvm-analysis-testFramework/src/com/intellij/jvm/analysis/testFramework/JvmInspectionTestBase.kt +++ b/jvm/jvm-analysis-testFramework/src/com/intellij/jvm/analysis/testFramework/JvmInspectionTestBase.kt @@ -136,7 +136,9 @@ abstract class JvmInspectionTestBase : LightJavaCodeInsightFixtureTestCase() { } private fun JavaCodeInsightTestFixture.getIntention(hint: String): IntentionAction { - return getAvailableIntention(hint) ?: throw AssertionError("Quickfix '$hint' is not available") + return getAvailableIntention(hint) ?: throw AssertionError( + "Quick-fix '$hint' not in list of available intentions:\n${availableIntentions.joinToString(separator = "\n") { it.text }}" + ) } protected fun JavaCodeInsightTestFixture.testQuickFixUnavailable(