ReplaceWith: suggest for "invoke" extension
#KT-8597 Fixed
This commit is contained in:
committed by
Yan Zhulanow
parent
bb7d4c224f
commit
be194c3460
@@ -18,6 +18,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.idea.intentions.isInvokeOperator
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
@@ -34,7 +35,14 @@ class CallableUsageReplacementStrategy(
|
||||
if (!resolvedCall.status.isSuccess) return null
|
||||
|
||||
val callElement = when (resolvedCall) {
|
||||
is VariableAsFunctionResolvedCall -> resolvedCall.variableCall.call.callElement
|
||||
is VariableAsFunctionResolvedCall -> {
|
||||
val callElement = resolvedCall.variableCall.call.callElement
|
||||
if (resolvedCall.resultingDescriptor.isInvokeOperator) {
|
||||
callElement.parent as? KtCallExpression ?: callElement
|
||||
} else {
|
||||
callElement
|
||||
}
|
||||
}
|
||||
else -> resolvedCall.call.callElement
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.idea.core.*
|
||||
import org.jetbrains.kotlin.idea.inspections.RedundantUnitExpressionInspection
|
||||
import org.jetbrains.kotlin.idea.intentions.InsertExplicitTypeArgumentsIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.isInvokeOperator
|
||||
import org.jetbrains.kotlin.idea.util.CommentSaver
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||
@@ -35,6 +36,7 @@ import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.isError
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
@@ -135,7 +137,23 @@ class CodeInliner<TCallElement : KtElement>(
|
||||
.forEach { ImportInsertHelper.getInstance(project).importDescriptor(file, it) }
|
||||
|
||||
val replacementPerformer = when (elementToBeReplaced) {
|
||||
is KtExpression -> ExpressionReplacementPerformer(codeToInline, elementToBeReplaced)
|
||||
is KtExpression -> {
|
||||
if (descriptor.isInvokeOperator) {
|
||||
val call = elementToBeReplaced as? KtCallExpression
|
||||
?: (elementToBeReplaced as? KtDotQualifiedExpression)?.selectorExpression as? KtCallExpression
|
||||
val callee = call?.calleeExpression
|
||||
if (callee != null && callee.text != OperatorNameConventions.INVOKE.asString()) {
|
||||
val receiverExpression = (codeToInline.mainExpression as? KtQualifiedExpression)?.receiverExpression
|
||||
when {
|
||||
elementToBeReplaced is KtCallExpression && receiverExpression is KtThisExpression ->
|
||||
receiverExpression.replace(callee)
|
||||
elementToBeReplaced is KtDotQualifiedExpression ->
|
||||
receiverExpression?.replace(psiFactory.createExpressionByPattern("$0.$1", receiverExpression, callee))
|
||||
}
|
||||
}
|
||||
}
|
||||
ExpressionReplacementPerformer(codeToInline, elementToBeReplaced)
|
||||
}
|
||||
is KtAnnotationEntry -> AnnotationEntryReplacementPerformer(codeToInline, elementToBeReplaced)
|
||||
is KtSuperTypeCallEntry -> SuperTypeCallEntryReplacementPerformer(codeToInline, elementToBeReplaced)
|
||||
else -> {
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.types.isFlexible
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
import org.jetbrains.kotlin.util.OperatorChecks
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
fun KtContainerNode.description(): String? {
|
||||
when (node.elementType) {
|
||||
@@ -364,3 +365,6 @@ fun KotlinType.reflectToRegularFunctionType(): KotlinType {
|
||||
if (isKSuspendFunctionType) builtIns.getSuspendFunction(parameterCount) else builtIns.getFunction(parameterCount)
|
||||
return KotlinTypeFactory.simpleNotNullType(annotations, classDescriptor, arguments)
|
||||
}
|
||||
|
||||
val CallableDescriptor.isInvokeOperator: Boolean
|
||||
get() = this is FunctionDescriptor && isOperator && name == OperatorNameConventions.INVOKE
|
||||
|
||||
+6
-2
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.idea.codeInliner.CallableUsageReplacementStrategy
|
||||
import org.jetbrains.kotlin.idea.codeInliner.ClassUsageReplacementStrategy
|
||||
import org.jetbrains.kotlin.idea.codeInliner.UsageReplacementStrategy
|
||||
import org.jetbrains.kotlin.idea.core.OptionalParametersHelper
|
||||
import org.jetbrains.kotlin.idea.intentions.isInvokeOperator
|
||||
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
|
||||
@@ -193,7 +194,10 @@ abstract class DeprecatedSymbolUsageFixBase(
|
||||
): UsageReplacementStrategy? {
|
||||
val resolutionFacade = element.getResolutionFacade()
|
||||
val bindingContext = resolutionFacade.analyze(element, BodyResolveMode.PARTIAL)
|
||||
var target = element.mainReference.resolveToDescriptors(bindingContext).singleOrNull() ?: return null
|
||||
val resolvedCall = element.getResolvedCall(bindingContext)
|
||||
var target = resolvedCall?.resultingDescriptor?.takeIf { it.isInvokeOperator }
|
||||
?: element.mainReference.resolveToDescriptors(bindingContext).singleOrNull()
|
||||
?: return null
|
||||
|
||||
var replacePatternFromSymbol =
|
||||
fetchReplaceWithPattern(target, resolutionFacade.project, element, replaceWith.replaceInWholeProject)
|
||||
@@ -208,7 +212,7 @@ abstract class DeprecatedSymbolUsageFixBase(
|
||||
|
||||
when (target) {
|
||||
is CallableDescriptor -> {
|
||||
val resolvedCall = element.getResolvedCall(bindingContext) ?: return null
|
||||
if (resolvedCall == null) return null
|
||||
if (!resolvedCall.isReallySuccess()) return null
|
||||
val replacement = ReplaceWithAnnotationAnalyzer.analyzeCallableReplacement(
|
||||
replaceWith, target, resolutionFacade, reformat
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Replace with 'execute(action)'" "true"
|
||||
|
||||
class Executor {
|
||||
@Deprecated("Use Executor.execute(Runnable) instead.", ReplaceWith("execute(action)"))
|
||||
operator fun invoke(action: () -> Unit) {}
|
||||
|
||||
fun execute(action: () -> Unit) {}
|
||||
}
|
||||
|
||||
fun usage(executor: Executor) {
|
||||
<caret>executor {
|
||||
// do something
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Replace with 'execute(action)'" "true"
|
||||
|
||||
class Executor {
|
||||
@Deprecated("Use Executor.execute(Runnable) instead.", ReplaceWith("execute(action)"))
|
||||
operator fun invoke(action: () -> Unit) {}
|
||||
|
||||
fun execute(action: () -> Unit) {}
|
||||
}
|
||||
|
||||
fun usage(executor: Executor) {
|
||||
executor.execute {
|
||||
// do something
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Replace with 'Foo.execute(action)'" "true"
|
||||
|
||||
class Executor {
|
||||
@Deprecated("Use Executor.execute(Runnable) instead.", ReplaceWith("Foo.execute(action)"))
|
||||
operator fun invoke(action: () -> Unit) {}
|
||||
|
||||
fun execute(action: () -> Unit) {}
|
||||
}
|
||||
|
||||
object Foo {
|
||||
fun execute(action: () -> Unit) {}
|
||||
}
|
||||
|
||||
fun usage(executor: Executor) {
|
||||
<caret>executor {
|
||||
// do something
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Replace with 'Foo.execute(action)'" "true"
|
||||
|
||||
class Executor {
|
||||
@Deprecated("Use Executor.execute(Runnable) instead.", ReplaceWith("Foo.execute(action)"))
|
||||
operator fun invoke(action: () -> Unit) {}
|
||||
|
||||
fun execute(action: () -> Unit) {}
|
||||
}
|
||||
|
||||
object Foo {
|
||||
fun execute(action: () -> Unit) {}
|
||||
}
|
||||
|
||||
fun usage(executor: Executor) {
|
||||
Foo.execute {
|
||||
// do something
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Replace with 'execute(action)'" "true"
|
||||
|
||||
class Executor {
|
||||
@Deprecated("Use Executor.execute(Runnable) instead.", ReplaceWith("execute(action)"))
|
||||
operator fun invoke(action: () -> Unit) {}
|
||||
|
||||
fun execute(action: () -> Unit) {}
|
||||
|
||||
fun usage(executor: Executor) {
|
||||
<caret>invoke {
|
||||
// do something
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Replace with 'execute(action)'" "true"
|
||||
|
||||
class Executor {
|
||||
@Deprecated("Use Executor.execute(Runnable) instead.", ReplaceWith("execute(action)"))
|
||||
operator fun invoke(action: () -> Unit) {}
|
||||
|
||||
fun execute(action: () -> Unit) {}
|
||||
|
||||
fun usage(executor: Executor) {
|
||||
execute {
|
||||
// do something
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// "Replace with 'execute(action)'" "true"
|
||||
|
||||
class Executor {
|
||||
val self: Executor
|
||||
get() = this
|
||||
|
||||
@Deprecated("Use Executor.execute(Runnable) instead.", ReplaceWith("execute(action)"))
|
||||
operator fun invoke(action: () -> Unit) {}
|
||||
|
||||
fun execute(action: () -> Unit) {}
|
||||
}
|
||||
|
||||
fun usage(executor: Executor) {
|
||||
executor.<caret>self {
|
||||
// do something
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// "Replace with 'execute(action)'" "true"
|
||||
|
||||
class Executor {
|
||||
val self: Executor
|
||||
get() = this
|
||||
|
||||
@Deprecated("Use Executor.execute(Runnable) instead.", ReplaceWith("execute(action)"))
|
||||
operator fun invoke(action: () -> Unit) {}
|
||||
|
||||
fun execute(action: () -> Unit) {}
|
||||
}
|
||||
|
||||
fun usage(executor: Executor) {
|
||||
executor.self.execute {
|
||||
// do something
|
||||
}
|
||||
}
|
||||
@@ -7061,6 +7061,26 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
runTest("idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/in.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("invoke.kt")
|
||||
public void testInvoke() throws Exception {
|
||||
runTest("idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/invoke.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("invoke2.kt")
|
||||
public void testInvoke2() throws Exception {
|
||||
runTest("idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/invoke2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("invoke3.kt")
|
||||
public void testInvoke3() throws Exception {
|
||||
runTest("idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/invoke3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("invoke4.kt")
|
||||
public void testInvoke4() throws Exception {
|
||||
runTest("idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/invoke4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("plusAssign.kt")
|
||||
public void testPlusAssign() throws Exception {
|
||||
runTest("idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls/plusAssign.kt");
|
||||
|
||||
Reference in New Issue
Block a user