From 14f63cdce523c3606531beaf27da064d78b142bd Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Fri, 24 Apr 2015 20:45:57 +0300 Subject: [PATCH] Change Signature: Substitute parameter references in default values of call expression arguments --- .../JetChangeSignatureDialog.java | 6 +- .../changeSignature/JetParameterInfo.kt | 62 +++++- .../usages/JetFunctionCallUsage.java | 197 ++++++++++++++++-- .../introduceParameter/partialSubstitution.kt | 9 + .../partialSubstitution.kt.after | 9 + .../substituteComplexExpressions.kt | 9 + .../substituteComplexExpressions.kt.after | 9 + .../substituteDoubleReceiver.kt | 18 ++ .../substituteDoubleReceiver.kt.after | 18 ++ .../substituteExplicitThisInMember.kt | 16 ++ .../substituteExplicitThisInMember.kt.after | 16 ++ .../substituteImplicitThisInMember.kt | 16 ++ .../substituteImplicitThisInMember.kt.after | 16 ++ .../substituteInPrimaryConstructorCall.kt | 19 ++ ...ubstituteInPrimaryConstructorCall.kt.after | 19 ++ .../substituteInSecondaryConstructorCall.kt | 18 ++ ...stituteInSecondaryConstructorCall.kt.after | 18 ++ .../introduceParameter/substituteInfixCall.kt | 10 + .../substituteInfixCall.kt.after | 10 + .../valueAtCallSite.kt.after | 2 +- .../valueAtCallSiteMultipleUsages.kt.after | 2 +- .../refactoring/introduceParameter/varargs.kt | 10 + .../introduceParameter/varargs.kt.after | 10 + .../introduce/JetExtractionTestGenerated.java | 54 +++++ 24 files changed, 549 insertions(+), 24 deletions(-) create mode 100644 idea/testData/refactoring/introduceParameter/partialSubstitution.kt create mode 100644 idea/testData/refactoring/introduceParameter/partialSubstitution.kt.after create mode 100644 idea/testData/refactoring/introduceParameter/substituteComplexExpressions.kt create mode 100644 idea/testData/refactoring/introduceParameter/substituteComplexExpressions.kt.after create mode 100644 idea/testData/refactoring/introduceParameter/substituteDoubleReceiver.kt create mode 100644 idea/testData/refactoring/introduceParameter/substituteDoubleReceiver.kt.after create mode 100644 idea/testData/refactoring/introduceParameter/substituteExplicitThisInMember.kt create mode 100644 idea/testData/refactoring/introduceParameter/substituteExplicitThisInMember.kt.after create mode 100644 idea/testData/refactoring/introduceParameter/substituteImplicitThisInMember.kt create mode 100644 idea/testData/refactoring/introduceParameter/substituteImplicitThisInMember.kt.after create mode 100644 idea/testData/refactoring/introduceParameter/substituteInPrimaryConstructorCall.kt create mode 100644 idea/testData/refactoring/introduceParameter/substituteInPrimaryConstructorCall.kt.after create mode 100644 idea/testData/refactoring/introduceParameter/substituteInSecondaryConstructorCall.kt create mode 100644 idea/testData/refactoring/introduceParameter/substituteInSecondaryConstructorCall.kt.after create mode 100644 idea/testData/refactoring/introduceParameter/substituteInfixCall.kt create mode 100644 idea/testData/refactoring/introduceParameter/substituteInfixCall.kt.after create mode 100644 idea/testData/refactoring/introduceParameter/varargs.kt create mode 100644 idea/testData/refactoring/introduceParameter/varargs.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureDialog.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureDialog.java index aaac774a021..9a1ab7859f5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureDialog.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureDialog.java @@ -54,6 +54,7 @@ import org.jetbrains.kotlin.descriptors.Visibilities; import org.jetbrains.kotlin.descriptors.Visibility; import org.jetbrains.kotlin.idea.JetFileType; import org.jetbrains.kotlin.idea.refactoring.JetRefactoringBundle; +import org.jetbrains.kotlin.psi.JetExpression; import org.jetbrains.kotlin.psi.JetExpressionCodeFragment; import org.jetbrains.kotlin.psi.JetTypeCodeFragment; import org.jetbrains.kotlin.types.JetType; @@ -459,7 +460,10 @@ public class JetChangeSignatureDialog extends ChangeSignatureDialogBase< parameter.setCurrentTypeText(myParametersTableModel.getItems().get(i).typeCodeFragment.getText().trim()); JetExpressionCodeFragment codeFragment = (JetExpressionCodeFragment) myParametersTableModel.getItems().get(i).defaultValueCodeFragment; - parameter.setDefaultValueForCall(codeFragment.getContentElement()); + JetExpression oldDefaultValue = parameter.getDefaultValueForCall(); + if (!codeFragment.getText().equals(oldDefaultValue != null ? oldDefaultValue.getText() : "")) { + parameter.setDefaultValueForCall(codeFragment.getContentElement()); + } } String returnTypeText = myReturnTypeCodeFragment != null ? myReturnTypeCodeFragment.getText().trim() : ""; diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetParameterInfo.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetParameterInfo.kt index c0e2e15eb01..ca57fc56852 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetParameterInfo.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetParameterInfo.kt @@ -17,16 +17,22 @@ package org.jetbrains.kotlin.idea.refactoring.changeSignature import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference import com.intellij.refactoring.changeSignature.ParameterInfo -import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.JetFunctionDefinitionUsage +import org.jetbrains.kotlin.idea.references.JetReference import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers -import org.jetbrains.kotlin.psi.JetExpression -import org.jetbrains.kotlin.psi.JetModifierList -import org.jetbrains.kotlin.psi.JetParameter +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver import org.jetbrains.kotlin.types.JetType +import java.util.LinkedHashMap public class JetParameterInfo( val functionDescriptor: FunctionDescriptor, @@ -41,6 +47,54 @@ public class JetParameterInfo( val originalType: JetType? = type var currentTypeText: String = getOldTypeText() + public val defaultValueParameterReferences: Map + + init { + val file = defaultValueForCall?.getContainingFile() as? JetFile + defaultValueParameterReferences = + if (defaultValueForCall != null && file != null && (file.isPhysical() || file.analysisContext != null)) { + val map = LinkedHashMap() + + defaultValueForCall!!.accept( + object : JetTreeVisitorVoid() { + private fun getRelevantDescriptor( + expression: JetSimpleNameExpression, + ref: JetReference + ): DeclarationDescriptor? { + val context = expression.analyze(BodyResolveMode.PARTIAL) + + val descriptor = ref.resolveToDescriptors(context).singleOrNull() + if (descriptor is ValueParameterDescriptor) return descriptor + + if (descriptor is PropertyDescriptor && functionDescriptor is ConstructorDescriptor) { + val parameter = DescriptorToSourceUtils.getSourceFromDescriptor(descriptor) as? JetParameter + return parameter?.let { context[BindingContext.VALUE_PARAMETER, it] } + } + + val resolvedCall = expression.getResolvedCall(context) ?: return null + (resolvedCall.getResultingDescriptor() as? ReceiverParameterDescriptor)?.let { return it } + + (resolvedCall.getExtensionReceiver() as? ThisReceiver)?.let { return it.getDeclarationDescriptor() } + (resolvedCall.getDispatchReceiver() as? ThisReceiver)?.let { return it.getDeclarationDescriptor() } + + return null + } + + override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) { + val ref = expression.getReference() as? JetReference ?: return + val descriptor = getRelevantDescriptor(expression, ref) ?: return + map[ref] = descriptor + } + } + ) + + map + } + else { + emptyMap() + } + } + private fun getOldTypeText() = originalType?.let { IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(it) } ?: "" override fun getOldIndex(): Int = originalIndex diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetFunctionCallUsage.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetFunctionCallUsage.java index ddcb49a80fa..27f22c11f86 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetFunctionCallUsage.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetFunctionCallUsage.java @@ -17,32 +17,63 @@ package org.jetbrains.kotlin.idea.refactoring.changeSignature.usages; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.containers.ContainerUtil; +import gnu.trove.TIntArrayList; +import gnu.trove.TIntProcedure; +import kotlin.Function1; import kotlin.KotlinPackage; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.descriptors.CallableDescriptor; -import org.jetbrains.kotlin.descriptors.ClassDescriptor; -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; +import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo; import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo; +import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.ExtractionEnginePackage; +import org.jetbrains.kotlin.idea.util.ShortenReferences; import org.jetbrains.kotlin.idea.util.psiModificationUtil.PsiModificationUtilPackage; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage; +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; +import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument; import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode; import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver; +import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver; +import org.jetbrains.kotlin.types.JetType; +import org.jetbrains.kotlin.types.checker.JetTypeChecker; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory; public class JetFunctionCallUsage extends JetUsageInfo { + private static final Comparator> + REVERSE_TEXT_OFFSET_COMPARATOR = new Comparator>() { + @Override + public int compare( + @NotNull Map.Entry o1, + @NotNull Map.Entry o2 + ) { + int offset1 = o1.getKey().getElement().getTextRange().getStartOffset(); + int offset2 = o2.getKey().getElement().getTextRange().getStartOffset(); + return offset1 < offset2 ? 1 + : offset1 > offset2 ? -1 + : 0; + } + }; + + private static final Function1 + SHORTEN_ARGUMENTS_OPTIONS = new Function1() { + @Override + public ShortenReferences.Options invoke(JetElement element) { + return new ShortenReferences.Options(true, true); + } + }; + private final JetFunctionDefinitionUsage callee; private final ResolvedCall resolvedCall; @@ -84,6 +115,111 @@ public class JetFunctionCallUsage extends JetUsageInfo { return true; } + @Nullable + private JetExpression getReceiverExpressionIfMatched( + @NotNull ReceiverValue receiverValue, + @NotNull DeclarationDescriptor originalDescriptor, + @NotNull JetPsiFactory psiFactory + ) { + if (!receiverValue.exists()) return null; + + // Replace descriptor of extension function/property with descriptor of its receiver + // to simplify checking against receiver value in the corresponding resolved call + if (originalDescriptor instanceof CallableDescriptor && !(originalDescriptor instanceof ReceiverParameterDescriptor)) { + ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) originalDescriptor).getExtensionReceiverParameter(); + if (receiverParameter == null) return null; + originalDescriptor = receiverParameter; + } + + boolean currentIsExtension = resolvedCall.getExtensionReceiver() == receiverValue; + boolean originalIsExtension = + originalDescriptor instanceof ReceiverParameterDescriptor && + ((ReceiverParameterDescriptor) originalDescriptor).getValue() instanceof ExtensionReceiver; + if (currentIsExtension != originalIsExtension) return null; + + JetType originalType = originalDescriptor instanceof ReceiverParameterDescriptor + ? ((ReceiverParameterDescriptor) originalDescriptor).getType() + : originalDescriptor instanceof ClassDescriptor + ? ((ClassDescriptor) originalDescriptor).getDefaultType() + : null; + if (originalType == null || !JetTypeChecker.DEFAULT.isSubtypeOf(receiverValue.getType(), originalType)) return null; + + return getReceiverExpression(receiverValue, psiFactory); + } + + @NotNull + private JetExpression substituteReferences( + @NotNull JetExpression expression, + @NotNull Map referenceMap, + @NotNull JetPsiFactory psiFactory + ) { + if (referenceMap.isEmpty() || resolvedCall == null) return expression; + + JetExpression newExpression = (JetExpression) expression.copy(); + + Map nameCounterpartMap = + ExtractionEnginePackage.createNameCounterpartMap(expression, newExpression); + + Map valueArguments = resolvedCall.getValueArguments(); + // Sort by descending offset so that call arguments are replaced before call itself + List> sortedEntries = + ContainerUtil.sorted(referenceMap.entrySet(), REVERSE_TEXT_OFFSET_COMPARATOR); + for (Map.Entry e : sortedEntries) { + DeclarationDescriptor descriptor = e.getValue(); + + JetExpression argumentExpression; + boolean addReceiver = false; + if (descriptor instanceof ValueParameterDescriptor) { // Ordinary parameter + // Find corresponding parameter in the current function (may differ from 'descriptor' if original function is part of override hierarchy) + ValueParameterDescriptor parameterDescriptor = + resolvedCall.getResultingDescriptor().getValueParameters().get(((ValueParameterDescriptor) descriptor).getIndex()); + + ResolvedValueArgument resolvedValueArgument = valueArguments.get(parameterDescriptor); + if (!(resolvedValueArgument instanceof ExpressionValueArgument)) continue; + + ValueArgument argument = ((ExpressionValueArgument) resolvedValueArgument).getValueArgument(); + if (argument == null) continue; + + argumentExpression = argument.getArgumentExpression(); + } + else { + addReceiver = !(descriptor instanceof ReceiverParameterDescriptor); + argumentExpression = getReceiverExpressionIfMatched(resolvedCall.getExtensionReceiver(), descriptor, psiFactory); + if (argumentExpression == null) { + argumentExpression = getReceiverExpressionIfMatched(resolvedCall.getDispatchReceiver(), descriptor, psiFactory); + } + } + if (argumentExpression == null) continue; + + //noinspection SuspiciousMethodCalls + JetExpression expressionToReplace = nameCounterpartMap.get(e.getKey().getElement()); + if (expressionToReplace == null) continue; + PsiElement parent = expressionToReplace.getParent(); + if (parent instanceof JetThisExpression) { + expressionToReplace = (JetThisExpression) parent; + } + + if (addReceiver) { + JetCallExpression callExpression = PsiTreeUtil.getParentOfType(expressionToReplace, JetCallExpression.class, true); + if (callExpression != null && PsiTreeUtil.isAncestor(callExpression.getCalleeExpression(), expressionToReplace, false)) { + expressionToReplace = callExpression; + } else { + // Do not substitute operation references in infix/prefix calls + if (parent instanceof JetOperationExpression + && ((JetOperationExpression) parent).getOperationReference() == expressionToReplace) { + continue; + } + } + expressionToReplace.replace(psiFactory.createExpression(argumentExpression.getText() + "." + expressionToReplace.getText())); + } + else { + expressionToReplace.replace(argumentExpression); + } + } + + return newExpression; + } + private void updateArgumentsAndReceiver(JetChangeInfo changeInfo, JetCallElement element) { JetValueArgumentList arguments = element.getValueArgumentList(); assert arguments != null : "Argument list is expected: " + element.getText(); @@ -93,15 +229,25 @@ public class JetFunctionCallUsage extends JetUsageInfo { StringBuilder parametersBuilder = new StringBuilder("("); boolean isFirst = true; + TIntArrayList indicesOfArgumentsWithDefaultValues = new TIntArrayList(); + + JetPsiFactory psiFactory = new JetPsiFactory(element.getProject()); + List newSignatureParameters = changeInfo.getNonReceiverParameters(); for (JetParameterInfo parameterInfo : newSignatureParameters) { - if (isFirst) + if (isFirst) { isFirst = false; - else + } + else { parametersBuilder.append(','); + } JetExpression defaultValueForCall = parameterInfo.getDefaultValueForCall(); - String defaultValueText = defaultValueForCall != null ? defaultValueForCall.getText() : ""; + String defaultValueText = defaultValueForCall != null + ? substituteReferences(defaultValueForCall, + parameterInfo.getDefaultValueParameterReferences(), + psiFactory).getText() + : ""; if (isNamedCall) { String newName = parameterInfo.getInheritedName(callee); @@ -112,10 +258,9 @@ public class JetFunctionCallUsage extends JetUsageInfo { } parametersBuilder.append(')'); - JetValueArgumentList newArguments = JetPsiFactory(getProject()).createCallArguments(parametersBuilder.toString()); + JetValueArgumentList newArgumentList = JetPsiFactory(getProject()).createCallArguments(parametersBuilder.toString()); Map argumentMap = getParamIndexToArgumentMap(changeInfo, oldArguments); - int argIndex = 0; JetParameterInfo newReceiverInfo = changeInfo.getReceiverParameterInfo(); JetParameterInfo originalReceiverInfo = changeInfo.getMethodDescriptor().getReceiver(); @@ -123,8 +268,6 @@ public class JetFunctionCallUsage extends JetUsageInfo { ReceiverValue extensionReceiver = resolvedCall != null ? resolvedCall.getExtensionReceiver() : ReceiverValue.NO_RECEIVER; ReceiverValue dispatchReceiver = resolvedCall != null ? resolvedCall.getDispatchReceiver() : ReceiverValue.NO_RECEIVER; - JetPsiFactory psiFactory = new JetPsiFactory(element.getProject()); - PsiElement elementToReplace = element; PsiElement parent = element.getParent(); if (parent instanceof JetQualifiedExpression && ((JetQualifiedExpression) parent).getSelectorExpression() == element) { @@ -136,13 +279,17 @@ public class JetFunctionCallUsage extends JetUsageInfo { && elementToReplace instanceof JetQualifiedExpression && dispatchReceiver instanceof ExpressionReceiver) return; - for (JetValueArgument newArgument : newArguments.getArguments()) { - JetParameterInfo parameterInfo = newSignatureParameters.get(argIndex++); + List newArguments = newArgumentList.getArguments(); + int actualIndex = 0; + for (int i = 0; i < newArguments.size(); i++) { + JetValueArgument newArgument = newArguments.get(i); + JetParameterInfo parameterInfo = newSignatureParameters.get(i); if (parameterInfo == originalReceiverInfo) { JetExpression receiverExpression = getReceiverExpression(extensionReceiver, psiFactory); if (receiverExpression != null) { newArgument.replace(receiverExpression); } + actualIndex++; continue; } @@ -166,6 +313,9 @@ public class JetFunctionCallUsage extends JetUsageInfo { newArgument.delete(); } } + else { + indicesOfArgumentsWithDefaultValues.add(actualIndex++); + } } List lambdaArguments = element.getFunctionLiteralArguments(); @@ -173,10 +323,23 @@ public class JetFunctionCallUsage extends JetUsageInfo { element.deleteChildRange(KotlinPackage.first(lambdaArguments), KotlinPackage.last(lambdaArguments)); } - JetValueArgument lastArgument = KotlinPackage.lastOrNull(newArguments.getArguments()); + JetValueArgument lastArgument = KotlinPackage.lastOrNull(newArgumentList.getArguments()); boolean hasTrailingLambdaInArgumentListAfter = lastArgument != null && lastArgument.getArgumentExpression() instanceof JetFunctionLiteralExpression; - arguments.replace(newArguments); + arguments = (JetValueArgumentList) arguments.replace(newArgumentList); + + final List argumentsToShorten = new ArrayList(indicesOfArgumentsWithDefaultValues.size()); + final List argumentList = arguments.getArguments(); + indicesOfArgumentsWithDefaultValues.forEach( + new TIntProcedure() { + @Override + public boolean execute(int i) { + argumentsToShorten.add(argumentList.get(i)); + return true; + } + } + ); + new ShortenReferences(SHORTEN_ARGUMENTS_OPTIONS).process(argumentsToShorten); JetElement newElement = element; if (newReceiverInfo != originalReceiverInfo) { diff --git a/idea/testData/refactoring/introduceParameter/partialSubstitution.kt b/idea/testData/refactoring/introduceParameter/partialSubstitution.kt new file mode 100644 index 00000000000..b4652cab4e5 --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/partialSubstitution.kt @@ -0,0 +1,9 @@ +// WITH_DEFAULT_VALUE: false +fun foo(a: Int): Int { + val b = 1 + return (a + b) * 2 +} + +fun test() { + foo(1) +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/partialSubstitution.kt.after b/idea/testData/refactoring/introduceParameter/partialSubstitution.kt.after new file mode 100644 index 00000000000..6a804b3bf3e --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/partialSubstitution.kt.after @@ -0,0 +1,9 @@ +// WITH_DEFAULT_VALUE: false +fun foo(i: Int): Int { + val b = 1 + return i * 2 +} + +fun test() { + foo(1 + b) +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteComplexExpressions.kt b/idea/testData/refactoring/introduceParameter/substituteComplexExpressions.kt new file mode 100644 index 00000000000..b5520602a40 --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteComplexExpressions.kt @@ -0,0 +1,9 @@ +// WITH_DEFAULT_VALUE: false + +fun foo(a: Int, b: Int): Int { + return a * b / 2 +} + +fun test() { + foo(1 + 2, 3 - 4) +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteComplexExpressions.kt.after b/idea/testData/refactoring/introduceParameter/substituteComplexExpressions.kt.after new file mode 100644 index 00000000000..f5970c32987 --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteComplexExpressions.kt.after @@ -0,0 +1,9 @@ +// WITH_DEFAULT_VALUE: false + +fun foo(i: Int): Int { + return i / 2 +} + +fun test() { + foo((1 + 2) * (3 - 4)) +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteDoubleReceiver.kt b/idea/testData/refactoring/introduceParameter/substituteDoubleReceiver.kt new file mode 100644 index 00000000000..af5d701758f --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteDoubleReceiver.kt @@ -0,0 +1,18 @@ +// WITH_DEFAULT_VALUE: false +public inline fun with(receiver: T, f: T.() -> R): R = receiver.f() + +class A(val a: Int) { + fun A.foo(): Int { + return (this@A.a + a) / 2 + } + + fun test() { + A(1).foo() + } +} + +fun test() { + val t = with(A(1)) { + A(2).foo() + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteDoubleReceiver.kt.after b/idea/testData/refactoring/introduceParameter/substituteDoubleReceiver.kt.after new file mode 100644 index 00000000000..e713d324ede --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteDoubleReceiver.kt.after @@ -0,0 +1,18 @@ +// WITH_DEFAULT_VALUE: false +public inline fun with(receiver: T, f: T.() -> R): R = receiver.f() + +class A(val a: Int) { + fun A.foo(i: Int): Int { + return i / 2 + } + + fun test() { + A(1).foo(a + A(1).a) + } +} + +fun test() { + val t = with(A(1)) { + A(2).foo(a + A(2).a) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteExplicitThisInMember.kt b/idea/testData/refactoring/introduceParameter/substituteExplicitThisInMember.kt new file mode 100644 index 00000000000..ada6d567b57 --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteExplicitThisInMember.kt @@ -0,0 +1,16 @@ +// WITH_DEFAULT_VALUE: false +public inline fun with(receiver: T, f: T.() -> R): R = receiver.f() + +class A(val a: Int) { + fun foo(x: A): Int { + return (this.a + x.a) / 2 + } +} + +fun test() { + A(1).foo(A(2)) + with(A(1)) { + foo(A(2)) + this.foo(A(2)) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteExplicitThisInMember.kt.after b/idea/testData/refactoring/introduceParameter/substituteExplicitThisInMember.kt.after new file mode 100644 index 00000000000..a515c72f39b --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteExplicitThisInMember.kt.after @@ -0,0 +1,16 @@ +// WITH_DEFAULT_VALUE: false +public inline fun with(receiver: T, f: T.() -> R): R = receiver.f() + +class A(val a: Int) { + fun foo(i: Int): Int { + return i / 2 + } +} + +fun test() { + A(1).foo(A(1).a + A(2).a) + with(A(1)) { + foo(a + A(2).a) + this.foo(a + A(2).a) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteImplicitThisInMember.kt b/idea/testData/refactoring/introduceParameter/substituteImplicitThisInMember.kt new file mode 100644 index 00000000000..bdc413f808a --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteImplicitThisInMember.kt @@ -0,0 +1,16 @@ +// WITH_DEFAULT_VALUE: false +public inline fun with(receiver: T, f: T.() -> R): R = receiver.f() + +class A(val a: Int) { + fun foo(x: A): Int { + return (a + x.a) / 2 + } +} + +fun test() { + A(1).foo(A(2)) + with(A(1)) { + foo(A(2)) + this.foo(A(2)) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteImplicitThisInMember.kt.after b/idea/testData/refactoring/introduceParameter/substituteImplicitThisInMember.kt.after new file mode 100644 index 00000000000..a515c72f39b --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteImplicitThisInMember.kt.after @@ -0,0 +1,16 @@ +// WITH_DEFAULT_VALUE: false +public inline fun with(receiver: T, f: T.() -> R): R = receiver.f() + +class A(val a: Int) { + fun foo(i: Int): Int { + return i / 2 + } +} + +fun test() { + A(1).foo(A(1).a + A(2).a) + with(A(1)) { + foo(a + A(2).a) + this.foo(a + A(2).a) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteInPrimaryConstructorCall.kt b/idea/testData/refactoring/introduceParameter/substituteInPrimaryConstructorCall.kt new file mode 100644 index 00000000000..3ad8804f933 --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteInPrimaryConstructorCall.kt @@ -0,0 +1,19 @@ +// WITH_DEFAULT_VALUE: false +// TARGET: +open class A(val a: Int) { + constructor(): this(1) + + fun foo(): Int { + return (a + 1) / 2 + } +} + +class B: A(1) { + +} + +class C: A { + constructor(n: Int): super(n + 1) +} + +fun test() = A(1) \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteInPrimaryConstructorCall.kt.after b/idea/testData/refactoring/introduceParameter/substituteInPrimaryConstructorCall.kt.after new file mode 100644 index 00000000000..6d7fcc360b2 --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteInPrimaryConstructorCall.kt.after @@ -0,0 +1,19 @@ +// WITH_DEFAULT_VALUE: false +// TARGET: +open class A(val a: Int, val i: Int) { + constructor(): this(1, 1 + 1) + + fun foo(): Int { + return i / 2 + } +} + +class B: A(1, 1 + 1) { + +} + +class C: A { + constructor(n: Int): super(n + 1, n + 1 + 1) +} + +fun test() = A(1, 1 + 1) \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteInSecondaryConstructorCall.kt b/idea/testData/refactoring/introduceParameter/substituteInSecondaryConstructorCall.kt new file mode 100644 index 00000000000..fbb9d84aa24 --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteInSecondaryConstructorCall.kt @@ -0,0 +1,18 @@ +// WITH_DEFAULT_VALUE: false +open class A { + constructor(): this(1) + + constructor(a: Int) { + val t = (a + 1) / 2 + } +} + +class B: A { + constructor(n: Int): super(n + 1) +} + +class C: A(1) { + +} + +fun test() = A(1) \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteInSecondaryConstructorCall.kt.after b/idea/testData/refactoring/introduceParameter/substituteInSecondaryConstructorCall.kt.after new file mode 100644 index 00000000000..b2ef43d8108 --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteInSecondaryConstructorCall.kt.after @@ -0,0 +1,18 @@ +// WITH_DEFAULT_VALUE: false +open class A { + constructor(): this(1 + 1) + + constructor(i: Int) { + val t = i / 2 + } +} + +class B: A { + constructor(n: Int): super(n + 1 + 1) +} + +class C: A(1 + 1) { + +} + +fun test() = A(1 + 1) \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteInfixCall.kt b/idea/testData/refactoring/introduceParameter/substituteInfixCall.kt new file mode 100644 index 00000000000..59673f7ed77 --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteInfixCall.kt @@ -0,0 +1,10 @@ +// WITH_DEFAULT_VALUE: false +fun foo(a: Int): Int { + fun Int.bar(n: Int) = this + n + + return (a bar 1) * 2 +} + +fun test() { + foo(2) +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/substituteInfixCall.kt.after b/idea/testData/refactoring/introduceParameter/substituteInfixCall.kt.after new file mode 100644 index 00000000000..a3504fc6c53 --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/substituteInfixCall.kt.after @@ -0,0 +1,10 @@ +// WITH_DEFAULT_VALUE: false +fun foo(i: Int): Int { + fun Int.bar(n: Int) = this + n + + return i * 2 +} + +fun test() { + foo(2 bar 1) +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/valueAtCallSite.kt.after b/idea/testData/refactoring/introduceParameter/valueAtCallSite.kt.after index e488d2d880b..567691ba788 100644 --- a/idea/testData/refactoring/introduceParameter/valueAtCallSite.kt.after +++ b/idea/testData/refactoring/introduceParameter/valueAtCallSite.kt.after @@ -5,5 +5,5 @@ fun foo(a: Int, i: Int): Int { } fun test() { - foo(1, a + 1) + foo(1, 1 + 1) } \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/valueAtCallSiteMultipleUsages.kt.after b/idea/testData/refactoring/introduceParameter/valueAtCallSiteMultipleUsages.kt.after index 59d2566ab1b..24f4e5f64c7 100644 --- a/idea/testData/refactoring/introduceParameter/valueAtCallSiteMultipleUsages.kt.after +++ b/idea/testData/refactoring/introduceParameter/valueAtCallSiteMultipleUsages.kt.after @@ -5,5 +5,5 @@ fun foo(i: Int): Int { } fun test() { - foo(a + 1) + foo(1 + 1) } \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/varargs.kt b/idea/testData/refactoring/introduceParameter/varargs.kt new file mode 100644 index 00000000000..84fe8bc008a --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/varargs.kt @@ -0,0 +1,10 @@ +// WITH_DEFAULT_VALUE: false +fun foo(vararg a: Int): Int { + return (a.size() + 1) * 2 +} + +fun test() { + foo() + foo(1) + foo(1, 2) +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceParameter/varargs.kt.after b/idea/testData/refactoring/introduceParameter/varargs.kt.after new file mode 100644 index 00000000000..1d8f873e49e --- /dev/null +++ b/idea/testData/refactoring/introduceParameter/varargs.kt.after @@ -0,0 +1,10 @@ +// WITH_DEFAULT_VALUE: false +fun foo(i: Int): Int { + return i * 2 +} + +fun test() { + foo(a.size() + 1) + foo(a.size() + 1) + foo(a.size() + 1) +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java index 0ad6dd177f5..6eeaa445e3c 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java @@ -2344,6 +2344,12 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { doIntroduceSimpleParameterTest(fileName); } + @TestMetadata("partialSubstitution.kt") + public void testPartialSubstitution() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/partialSubstitution.kt"); + doIntroduceSimpleParameterTest(fileName); + } + @TestMetadata("propertyAccessor.kt") public void testPropertyAccessor() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/propertyAccessor.kt"); @@ -2362,6 +2368,48 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { doIntroduceSimpleParameterTest(fileName); } + @TestMetadata("substituteComplexExpressions.kt") + public void testSubstituteComplexExpressions() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/substituteComplexExpressions.kt"); + doIntroduceSimpleParameterTest(fileName); + } + + @TestMetadata("substituteDoubleReceiver.kt") + public void testSubstituteDoubleReceiver() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/substituteDoubleReceiver.kt"); + doIntroduceSimpleParameterTest(fileName); + } + + @TestMetadata("substituteExplicitThisInMember.kt") + public void testSubstituteExplicitThisInMember() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/substituteExplicitThisInMember.kt"); + doIntroduceSimpleParameterTest(fileName); + } + + @TestMetadata("substituteImplicitThisInMember.kt") + public void testSubstituteImplicitThisInMember() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/substituteImplicitThisInMember.kt"); + doIntroduceSimpleParameterTest(fileName); + } + + @TestMetadata("substituteInPrimaryConstructorCall.kt") + public void testSubstituteInPrimaryConstructorCall() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/substituteInPrimaryConstructorCall.kt"); + doIntroduceSimpleParameterTest(fileName); + } + + @TestMetadata("substituteInSecondaryConstructorCall.kt") + public void testSubstituteInSecondaryConstructorCall() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/substituteInSecondaryConstructorCall.kt"); + doIntroduceSimpleParameterTest(fileName); + } + + @TestMetadata("substituteInfixCall.kt") + public void testSubstituteInfixCall() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/substituteInfixCall.kt"); + doIntroduceSimpleParameterTest(fileName); + } + @TestMetadata("throw.kt") public void testThrow() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/throw.kt"); @@ -2380,6 +2428,12 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { doIntroduceSimpleParameterTest(fileName); } + @TestMetadata("varargs.kt") + public void testVarargs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/varargs.kt"); + doIntroduceSimpleParameterTest(fileName); + } + @TestMetadata("while.kt") public void testWhile() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceParameter/while.kt");