Extract Function: Fix callee replacement in operation expressions
#KT-8294 Fixed
This commit is contained in:
@@ -42,10 +42,12 @@ public class CodeInsightUtils {
|
||||
@Nullable
|
||||
public static JetExpression findExpression(@NotNull PsiFile file, int startOffset, int endOffset) {
|
||||
PsiElement element = findElementOfClassAtRange(file, startOffset, endOffset, JetExpression.class);
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
else if (!(element instanceof JetExpression)) {
|
||||
if (element == null) return null;
|
||||
|
||||
// TODO: Support binary operations in "Introduce..." refactorings
|
||||
if (element instanceof JetOperationReferenceExpression
|
||||
&& ((JetOperationReferenceExpression) element).getReferencedNameElementType() != JetTokens.IDENTIFIER
|
||||
&& element.getParent() instanceof JetBinaryExpression) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,12 +27,18 @@ public class InfixCallToOrdinaryIntention : JetSelfTargetingIntention<JetBinaryE
|
||||
}
|
||||
|
||||
override fun applyTo(element: JetBinaryExpression, editor: Editor) {
|
||||
val argument = JetPsiUtil.safeDeparenthesize(element.getRight()!!)
|
||||
val pattern = "$0.$1" + when (argument) {
|
||||
is JetFunctionLiteralExpression -> " $2:'{}'"
|
||||
else -> "($2)"
|
||||
convert(element)
|
||||
}
|
||||
|
||||
companion object {
|
||||
public fun convert(element: JetBinaryExpression): JetExpression {
|
||||
val argument = JetPsiUtil.safeDeparenthesize(element.getRight()!!)
|
||||
val pattern = "$0.$1" + when (argument) {
|
||||
is JetFunctionLiteralExpression -> " $2:'{}'"
|
||||
else -> "($2)"
|
||||
}
|
||||
val replacement = JetPsiFactory(element).createExpressionByPattern(pattern, element.getLeft()!!, element.getOperationReference().getText(), argument)
|
||||
return element.replace(replacement) as JetExpression
|
||||
}
|
||||
val replacement = JetPsiFactory(element).createExpressionByPattern(pattern, element.getLeft()!!, element.getOperationReference().getText(), argument)
|
||||
element.replace(replacement)
|
||||
}
|
||||
}
|
||||
|
||||
+11
-1
@@ -59,6 +59,7 @@ import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.approximateWithResolvableType
|
||||
import org.jetbrains.kotlin.idea.util.isResolvableInScope
|
||||
import org.jetbrains.kotlin.lexer.JetToken
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
@@ -79,6 +80,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.asJetScope
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.DFS.CollectingNodeHandler
|
||||
@@ -703,7 +705,15 @@ private fun ExtractionData.inferParametersInfo(
|
||||
}
|
||||
else {
|
||||
val argumentExpr = (thisExpr ?: ref).getQualifiedExpressionForSelectorOrThis()
|
||||
argumentExpr.getText() ?: throw AssertionError("'this' reference shouldn't be empty: code fragment = $codeFragmentText")
|
||||
if (argumentExpr is JetOperationReferenceExpression) {
|
||||
val nameElement = argumentExpr.getReferencedNameElement()
|
||||
val nameElementType = nameElement.node.elementType
|
||||
(nameElementType as? JetToken)?.let {
|
||||
OperatorConventions.getNameForOperationSymbol(it)?.asString()
|
||||
} ?: nameElement.getText()
|
||||
}
|
||||
else argumentExpr.getText()
|
||||
?: throw AssertionError("reference shouldn't be empty: code fragment = $codeFragmentText")
|
||||
}
|
||||
if (extractFunctionRef) {
|
||||
val receiverTypeText = (originalDeclaration as JetCallableDeclaration).getReceiverTypeReference()?.getText() ?: ""
|
||||
|
||||
+18
-6
@@ -19,12 +19,11 @@ package org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.idea.core.appendElement
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.core.*
|
||||
import org.jetbrains.kotlin.idea.core.refactoring.isMultiLine
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.intentions.ConvertToExpressionBodyIntention
|
||||
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
|
||||
import org.jetbrains.kotlin.idea.intentions.InfixCallToOrdinaryIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.ExpressionValue
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.Initializer
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.Jump
|
||||
@@ -38,7 +37,6 @@ import org.jetbrains.kotlin.idea.util.psi.patternMatching.JetPsiUnifier
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.UnificationResult.StronglyMatched
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.UnificationResult.WeaklyMatched
|
||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.UnifierParameter
|
||||
import org.jetbrains.kotlin.idea.core.moveInsideParenthesesAndReplaceWith
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.JetPsiFactory.CallableBuilder
|
||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.DEBUG_TYPE_REFERENCE_STRING
|
||||
@@ -46,9 +44,9 @@ import org.jetbrains.kotlin.psi.codeFragmentUtil.debugTypeInfo
|
||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
import org.jetbrains.kotlin.types.isNullabilityFlexible
|
||||
import java.util.ArrayList
|
||||
import java.util.Collections
|
||||
import java.util.HashMap
|
||||
@@ -226,6 +224,20 @@ private fun makeCall(
|
||||
functionLiteralArgument.moveInsideParenthesesAndReplaceWith(wrappedCall, extractableDescriptor.originalContext)
|
||||
return
|
||||
}
|
||||
|
||||
if (anchor is JetOperationReferenceExpression) {
|
||||
val operationExpression = anchor.parent as? JetOperationExpression ?: return
|
||||
val newNameExpression = when (operationExpression) {
|
||||
is JetUnaryExpression -> OperatorToFunctionIntention.convert(operationExpression).second
|
||||
is JetBinaryExpression -> {
|
||||
InfixCallToOrdinaryIntention.convert(operationExpression).getCalleeExpressionIfAny()
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
newNameExpression?.replace(wrappedCall)
|
||||
return
|
||||
}
|
||||
|
||||
anchor.replace(wrappedCall)
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -209,7 +209,12 @@ public open class KotlinIntroduceParameterHandler(
|
||||
open fun invoke(project: Project, editor: Editor, expression: JetExpression, targetParent: JetNamedDeclaration) {
|
||||
val context = expression.analyze()
|
||||
|
||||
val expressionType = context.getType(expression)!!
|
||||
val expressionType = context.getType(expression)
|
||||
if (expressionType == null) {
|
||||
showErrorHint(project, editor, "Expression has no type", INTRODUCE_PARAMETER)
|
||||
return
|
||||
}
|
||||
|
||||
if (expressionType.isUnit() || expressionType.isNothing()) {
|
||||
val message = JetRefactoringBundle.message(
|
||||
"cannot.introduce.parameter.of.0.type",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fun foo(minus : String.(String) -> Unit) {
|
||||
"A" <selection>-</selection> "B"
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
Cannot perform refactoring without an expression
|
||||
@@ -0,0 +1,6 @@
|
||||
// PARAM_DESCRIPTOR: value-parameter val x: kotlin.String.(kotlin.String) -> kotlin.Unit defined in foo
|
||||
// PARAM_TYPES: kotlin.String.(kotlin.String) -> kotlin.Unit
|
||||
|
||||
fun foo(x : String.(String) -> Unit) {
|
||||
"A" <selection>x</selection> "B"
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// PARAM_DESCRIPTOR: value-parameter val x: kotlin.String.(kotlin.String) -> kotlin.Unit defined in foo
|
||||
// PARAM_TYPES: kotlin.String.(kotlin.String) -> kotlin.Unit
|
||||
|
||||
fun foo(x : String.(String) -> Unit) {
|
||||
"A".(function(x))("B")
|
||||
}
|
||||
|
||||
private fun function(x: String.(String) -> Unit) = x
|
||||
@@ -0,0 +1,6 @@
|
||||
// PARAM_DESCRIPTOR: value-parameter val plus: kotlin.String.() -> kotlin.Unit defined in foo
|
||||
// PARAM_TYPES: kotlin.String.() -> kotlin.Unit
|
||||
|
||||
fun foo(plus: String.() -> Unit) {
|
||||
<selection>+</selection> "A"
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// PARAM_DESCRIPTOR: value-parameter val plus: kotlin.String.() -> kotlin.Unit defined in foo
|
||||
// PARAM_TYPES: kotlin.String.() -> kotlin.Unit
|
||||
|
||||
fun foo(plus: String.() -> Unit) {
|
||||
"A".(function(plus))()
|
||||
}
|
||||
|
||||
private fun function(plus: String.() -> Unit) = plus
|
||||
+18
@@ -456,6 +456,24 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("convertBinaryExpression.kt")
|
||||
public void testConvertBinaryExpression() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/basic/convertBinaryExpression.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("convertInfixExpression.kt")
|
||||
public void testConvertInfixExpression() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/basic/convertInfixExpression.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("convertUnaryExpression.kt")
|
||||
public void testConvertUnaryExpression() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/basic/convertUnaryExpression.kt");
|
||||
doExtractFunctionTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("delegatingFunction.kt")
|
||||
public void testDelegatingFunction() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/basic/delegatingFunction.kt");
|
||||
|
||||
Reference in New Issue
Block a user