J2K: JetFunctionCallUsage

This commit is contained in:
Alexey Sedunov
2015-11-02 19:39:43 +03:00
parent ff86042f58
commit b334f92748
@@ -14,582 +14,463 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.refactoring.changeSignature.usages; package org.jetbrains.kotlin.idea.refactoring.changeSignature.usages
import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference
import com.intellij.psi.PsiReference; import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.usageView.UsageInfo
import com.intellij.usageView.UsageInfo; import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.ContainerUtil; import gnu.trove.TIntArrayList
import gnu.trove.TIntArrayList; import org.jetbrains.kotlin.descriptors.*
import gnu.trove.TIntProcedure; import org.jetbrains.kotlin.idea.caches.resolve.analyze
import kotlin.ArraysKt; import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
import kotlin.CollectionsKt; import org.jetbrains.kotlin.idea.core.moveFunctionLiteralOutsideParentheses
import kotlin.Pair; import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo
import kotlin.Unit; import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo
import kotlin.jvm.functions.Function1; import org.jetbrains.kotlin.idea.refactoring.changeSignature.isInsideOfCallerBody
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.createNameCounterpartMap
import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils; import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.idea.codeInsight.shorten.ShortenWaitingSetKt; import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.idea.core.PsiModificationUtilsKt; import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.idea.refactoring.changeSignature.ChangeSignatureUtilsKt; import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo; import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo; import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.ExtractorUtilKt; import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler; import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.idea.util.ShortenReferences; import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor; import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.resolve.calls.model.*; import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode; import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver; import org.jetbrains.kotlin.utils.sure
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import java.util.*
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
import org.jetbrains.kotlin.types.expressions.OperatorConventions;
import java.util.*; class JetFunctionCallUsage(
element: KtCallElement,
private val callee: JetCallableDefinitionUsage<*>
) : JetUsageInfo<KtCallElement>(element) {
private val context = element.analyze(BodyResolveMode.FULL)
private val resolvedCall = element.getResolvedCall(context)
public class JetFunctionCallUsage extends JetUsageInfo<KtCallElement> { override fun processUsage(changeInfo: JetChangeInfo, element: KtCallElement, allUsages: Array<out UsageInfo>): Boolean {
private static final Comparator<Pair<KtElement, KtElement>> if (shouldSkipUsage(element)) return true
REVERSED_TEXT_OFFSET_COMPARATOR = new Comparator<Pair<KtElement, KtElement>>() {
@Override
public int compare(
@NotNull Pair<KtElement, KtElement> p1,
@NotNull Pair<KtElement, KtElement> p2
) {
int offset1 = p1.getFirst().getTextRange().getStartOffset();
int offset2 = p2.getFirst().getTextRange().getStartOffset();
return offset1 < offset2 ? 1
: offset1 > offset2 ? -1
: 0;
}
};
private static final ShortenReferences.Options SHORTEN_ARGUMENTS_OPTIONS = new ShortenReferences.Options(true, true); changeNameIfNeeded(changeInfo, element)
private final JetCallableDefinitionUsage<?> callee; if (element.valueArgumentList != null) {
private final BindingContext context; if (changeInfo.isParameterSetOrOrderChanged) {
private final ResolvedCall<? extends CallableDescriptor> resolvedCall; updateArgumentsAndReceiver(changeInfo, element, allUsages)
public JetFunctionCallUsage(@NotNull KtCallElement element, JetCallableDefinitionUsage callee) {
super(element);
this.callee = callee;
this.context = ResolutionUtils.analyze(element, BodyResolveMode.FULL);
this.resolvedCall = CallUtilKt.getResolvedCall(element, context);
}
@Override
public boolean processUsage(@NotNull JetChangeInfo changeInfo, @NotNull KtCallElement element, @NotNull UsageInfo[] allUsages) {
if (shouldSkipUsage(element)) return true;
changeNameIfNeeded(changeInfo, element);
if (element.getValueArgumentList() != null) {
if (changeInfo.isParameterSetOrOrderChanged()) {
updateArgumentsAndReceiver(changeInfo, element, allUsages);
} }
else { else {
changeArgumentNames(changeInfo, element); changeArgumentNames(changeInfo, element)
} }
} }
if (changeInfo.getNewParametersCount() == 0 if (changeInfo.getNewParametersCount() == 0 && element is KtDelegatorToSuperCall) {
&& element instanceof KtDelegatorToSuperCall) { val enumEntry = element.getStrictParentOfType<KtEnumEntry>()
KtEnumEntry enumEntry = PsiTreeUtil.getParentOfType(element, KtEnumEntry.class, true); if (enumEntry != null && enumEntry.initializerList == element.parent) {
if (enumEntry != null && enumEntry.getInitializerList() == element.getParent()) { val initializerList = enumEntry.initializerList
PsiElement colon = enumEntry.getColon(); enumEntry.deleteChildRange(enumEntry.getColon() ?: initializerList, initializerList)
KtInitializerList initializerList = enumEntry.getInitializerList();
enumEntry.deleteChildRange(colon != null ? colon : initializerList, initializerList);
} }
} }
return true; return true
} }
private boolean shouldSkipUsage(KtCallElement element) { private fun shouldSkipUsage(element: KtCallElement): Boolean {
// TODO: We probable need more clever processing of invalid calls, but for now default to Java-like behaviour // TODO: We probable need more clever processing of invalid calls, but for now default to Java-like behaviour
if (resolvedCall == null && !(element instanceof KtDelegatorToSuperCall)) return true; if (resolvedCall == null && element !is KtDelegatorToSuperCall) return true
if (resolvedCall != null && !ArgumentMappingKt.isReallySuccess(resolvedCall)) { if (resolvedCall == null || resolvedCall.isReallySuccess()) return false
// TODO: investigate why arguments are not recorded for enum constructor call
if (element instanceof KtDelegatorToSuperCall && element.getParent().getParent() instanceof KtEnumEntry) return false; // TODO: investigate why arguments are not recorded for enum constructor call
for (ValueArgument valueArgument : resolvedCall.getCall().getValueArguments()) { if (element is KtDelegatorToSuperCall && element.parent.parent is KtEnumEntry) return false
if (!(resolvedCall.getArgumentMapping(valueArgument) instanceof ArgumentMatch)) return true;
} if (!resolvedCall.call.valueArguments.all{ resolvedCall.getArgumentMapping(it) is ArgumentMatch }) return true
Map<ValueParameterDescriptor, ResolvedValueArgument> arguments = resolvedCall.getValueArguments();
for (ValueParameterDescriptor valueParameter : resolvedCall.getResultingDescriptor().getValueParameters()) { val arguments = resolvedCall.valueArguments
if (!arguments.containsKey(valueParameter)) return true; return !resolvedCall.resultingDescriptor.valueParameters.all{ arguments.containsKey(it) }
}
}
return false;
} }
private boolean isPropertyJavaUsage() { private val isPropertyJavaUsage: Boolean
PsiElement calleeElement = this.callee.getElement(); get() {
return (calleeElement instanceof KtProperty || calleeElement instanceof KtParameter) val calleeElement = this.callee.element
&& resolvedCall != null && resolvedCall.getResultingDescriptor() instanceof JavaMethodDescriptor; if (calleeElement !is KtProperty && calleeElement !is KtParameter) return false
} return resolvedCall?.resultingDescriptor is JavaMethodDescriptor
protected void changeNameIfNeeded(JetChangeInfo changeInfo, KtCallElement element) {
if (!changeInfo.isNameChanged()) return;
KtExpression callee = element.getCalleeExpression();
if (!(callee instanceof KtSimpleNameExpression)) return;
String newName = changeInfo.getNewName();
if (isPropertyJavaUsage()) {
String currentName = ((KtSimpleNameExpression) callee).getReferencedName();
if (JvmAbi.isGetterName(currentName)) newName = JvmAbi.getterName(newName);
else if (JvmAbi.isSetterName(currentName)) newName = JvmAbi.setterName(newName);
} }
callee.replace(KtPsiFactoryKt.KtPsiFactory(getProject()).createSimpleName(newName)); protected fun changeNameIfNeeded(changeInfo: JetChangeInfo, element: KtCallElement) {
if (!changeInfo.isNameChanged) return
val callee = element.calleeExpression
if (callee !is KtSimpleNameExpression) return
var newName = changeInfo.newName
if (isPropertyJavaUsage) {
val currentName = callee.getReferencedName()
if (JvmAbi.isGetterName(currentName))
newName = JvmAbi.getterName(newName)
else if (JvmAbi.isSetterName(currentName)) newName = JvmAbi.setterName(newName)
}
callee.replace(KtPsiFactory(project).createSimpleName(newName))
} }
@Nullable private fun getReceiverExpressionIfMatched(
private KtExpression getReceiverExpressionIfMatched( receiverValue: ReceiverValue,
@NotNull ReceiverValue receiverValue, originalDescriptor: DeclarationDescriptor,
@NotNull DeclarationDescriptor originalDescriptor, psiFactory: KtPsiFactory
@NotNull KtPsiFactory psiFactory ): KtExpression? {
) { if (!receiverValue.exists()) return null
if (!receiverValue.exists()) return null;
// Replace descriptor of extension function/property with descriptor of its receiver // Replace descriptor of extension function/property with descriptor of its receiver
// to simplify checking against receiver value in the corresponding resolved call // to simplify checking against receiver value in the corresponding resolved call
if (originalDescriptor instanceof CallableDescriptor && !(originalDescriptor instanceof ReceiverParameterDescriptor)) { val adjustedDescriptor = if (originalDescriptor is CallableDescriptor && originalDescriptor !is ReceiverParameterDescriptor) {
ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) originalDescriptor).getExtensionReceiverParameter(); originalDescriptor.extensionReceiverParameter ?: return null
if (receiverParameter == null) return null;
originalDescriptor = receiverParameter;
} }
else originalDescriptor
boolean currentIsExtension = resolvedCall.getExtensionReceiver() == receiverValue; val currentIsExtension = resolvedCall!!.extensionReceiver == receiverValue
boolean originalIsExtension = val originalIsExtension = adjustedDescriptor is ReceiverParameterDescriptor && adjustedDescriptor.value is ExtensionReceiver
originalDescriptor instanceof ReceiverParameterDescriptor && if (currentIsExtension != originalIsExtension) return null
((ReceiverParameterDescriptor) originalDescriptor).getValue() instanceof ExtensionReceiver;
if (currentIsExtension != originalIsExtension) return null;
KotlinType originalType = originalDescriptor instanceof ReceiverParameterDescriptor val originalType = when (adjustedDescriptor) {
? ((ReceiverParameterDescriptor) originalDescriptor).getType() is ReceiverParameterDescriptor -> adjustedDescriptor.type
: originalDescriptor instanceof ClassDescriptor is ClassDescriptor -> adjustedDescriptor.defaultType
? ((ClassDescriptor) originalDescriptor).getDefaultType() else -> null
: null; }
if (originalType == null || !KotlinTypeChecker.DEFAULT.isSubtypeOf(receiverValue.getType(), originalType)) return null; if (originalType == null || !KotlinTypeChecker.DEFAULT.isSubtypeOf(receiverValue.type, originalType)) return null
return getReceiverExpression(receiverValue, psiFactory); return getReceiverExpression(receiverValue, psiFactory)
} }
private boolean needSeparateVariable(@NotNull PsiElement element) { private fun needSeparateVariable(element: PsiElement): Boolean {
if (element instanceof KtConstantExpression) return false; return when {
if (element instanceof KtThisExpression) return false; element is KtConstantExpression, element is KtThisExpression, element is KtSimpleNameExpression -> false
if (element instanceof KtSimpleNameExpression) return false; element is KtBinaryExpression && OperatorConventions.ASSIGNMENT_OPERATIONS.containsKeyRaw(element.operationToken) -> true
element is KtUnaryExpression && OperatorConventions.INCREMENT_OPERATIONS.containsRaw(element.operationToken) -> true
//noinspection SuspiciousMethodCalls element is KtCallExpression -> element.getResolvedCall(context)?.resultingDescriptor is ConstructorDescriptor
if (element instanceof KtBinaryExpression else -> element.children.any { needSeparateVariable(it) }
&& OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(((KtBinaryExpression) element).getOperationToken())) return true;
//noinspection SuspiciousMethodCalls
if (element instanceof KtUnaryExpression
&& OperatorConventions.INCREMENT_OPERATIONS.contains(((KtUnaryExpression) element).getOperationToken())) return true;
if (element instanceof KtCallExpression) {
ResolvedCall<? extends CallableDescriptor> resolvedCall =
CallUtilKt.getResolvedCall((KtCallExpression) element, context);
return resolvedCall != null && resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor;
} }
for (PsiElement child : element.getChildren()) {
if (needSeparateVariable(child)) return true;
}
return false;
} }
@NotNull private fun substituteReferences(
private KtExpression substituteReferences( expression: KtExpression,
@NotNull KtExpression expression, referenceMap: Map<PsiReference, DeclarationDescriptor>,
@NotNull Map<PsiReference, DeclarationDescriptor> referenceMap, psiFactory: KtPsiFactory
@NotNull final KtPsiFactory psiFactory ): KtExpression {
) { if (referenceMap.isEmpty() || resolvedCall == null) return expression
if (referenceMap.isEmpty() || resolvedCall == null) return expression;
KtExpression newExpression = (KtExpression) expression.copy(); val newExpression = expression.copy() as KtExpression
Map<KtSimpleNameExpression, KtSimpleNameExpression> nameCounterpartMap = val nameCounterpartMap = createNameCounterpartMap(expression, newExpression)
ExtractorUtilKt.createNameCounterpartMap(expression, newExpression);
Map<ValueParameterDescriptor, ResolvedValueArgument> valueArguments = resolvedCall.getValueArguments(); val valueArguments = resolvedCall.valueArguments
List<Pair<KtElement, KtElement>> replacements = new ArrayList<Pair<KtElement, KtElement>>(); val replacements = ArrayList<Pair<KtElement, KtElement>>()
for (Map.Entry<PsiReference, DeclarationDescriptor> e : referenceMap.entrySet()) { loop@ for ((ref, descriptor) in referenceMap.entries) {
DeclarationDescriptor descriptor = e.getValue(); var argumentExpression: KtExpression?
val addReceiver: Boolean
KtExpression argumentExpression; if (descriptor is ValueParameterDescriptor) {
boolean addReceiver = false; // Ordinary parameter
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) // Find corresponding parameter in the current function (may differ from 'descriptor' if original function is part of override hierarchy)
ValueParameterDescriptor parameterDescriptor = val parameterDescriptor = resolvedCall.resultingDescriptor.valueParameters[descriptor.index]
resolvedCall.getResultingDescriptor().getValueParameters().get(((ValueParameterDescriptor) descriptor).getIndex()); val resolvedValueArgument = valueArguments[parameterDescriptor] as? ExpressionValueArgument ?: continue
val argument = resolvedValueArgument.valueArgument ?: continue
ResolvedValueArgument resolvedValueArgument = valueArguments.get(parameterDescriptor); addReceiver = false
if (!(resolvedValueArgument instanceof ExpressionValueArgument)) continue; argumentExpression = argument.getArgumentExpression()
ValueArgument argument = ((ExpressionValueArgument) resolvedValueArgument).getValueArgument();
if (argument == null) continue;
argumentExpression = argument.getArgumentExpression();
} }
else { else {
addReceiver = !(descriptor instanceof ReceiverParameterDescriptor); addReceiver = descriptor !is ReceiverParameterDescriptor
argumentExpression = getReceiverExpressionIfMatched(resolvedCall.getExtensionReceiver(), descriptor, psiFactory); argumentExpression = getReceiverExpressionIfMatched(resolvedCall.extensionReceiver, descriptor, psiFactory)
if (argumentExpression == null) { ?: getReceiverExpressionIfMatched(resolvedCall.dispatchReceiver, descriptor, psiFactory)
argumentExpression = getReceiverExpressionIfMatched(resolvedCall.getDispatchReceiver(), descriptor, psiFactory); }
if (argumentExpression == null) continue
if (needSeparateVariable(argumentExpression)
&& PsiTreeUtil.getNonStrictParentOfType(element,
KtConstructorDelegationCall::class.java,
KtDelegationSpecifier::class.java,
KtParameter::class.java) == null) {
KotlinIntroduceVariableHandler.doRefactoring(project, null, argumentExpression, listOf(argumentExpression)) {
argumentExpression = psiFactory.createExpression(it.name!!)
} }
} }
if (argumentExpression == null) continue;
//noinspection unchecked var expressionToReplace: KtExpression = nameCounterpartMap.getRaw(ref.element) ?: continue
if (needSeparateVariable(argumentExpression) && val parent = expressionToReplace.parent
PsiTreeUtil.getNonStrictParentOfType(getElement(),
KtConstructorDelegationCall.class,
KtDelegationSpecifier.class,
KtParameter.class) == null) {
final Ref<KtExpression> newExpressionRef = new Ref<KtExpression>(); if (parent is KtThisExpression) {
KotlinIntroduceVariableHandler.doRefactoring( expressionToReplace = parent
getProject(),
null,
argumentExpression,
Collections.singletonList(argumentExpression),
new Function1<KtProperty, Unit>() {
@Override
public Unit invoke(KtProperty property) {
//noinspection ConstantConditions
newExpressionRef.set(psiFactory.createExpression(property.getName()));
return null;
}
}
);
argumentExpression = newExpressionRef.get();
}
//noinspection SuspiciousMethodCalls
KtExpression expressionToReplace = nameCounterpartMap.get(e.getKey().getElement());
if (expressionToReplace == null) continue;
PsiElement parent = expressionToReplace.getParent();
if (parent instanceof KtThisExpression) {
expressionToReplace = (KtThisExpression) parent;
} }
if (addReceiver) { if (addReceiver) {
KtCallExpression callExpression = PsiTreeUtil.getParentOfType(expressionToReplace, KtCallExpression.class, true); val callExpression = expressionToReplace.getParentOfTypeAndBranch<KtCallExpression>(true) { calleeExpression }
if (callExpression != null && PsiTreeUtil.isAncestor(callExpression.getCalleeExpression(), expressionToReplace, false)) { when {
expressionToReplace = callExpression; callExpression != null -> expressionToReplace = callExpression
} else { parent is KtOperationExpression && parent.operationReference == expressionToReplace -> continue@loop
// Do not substitute operation references in infix/prefix calls
if (parent instanceof KtOperationExpression
&& ((KtOperationExpression) parent).getOperationReference() == expressionToReplace) {
continue;
}
} }
replacements.add(
new Pair<KtElement, KtElement>( val replacement = psiFactory.createExpression("${argumentExpression!!.text}.${expressionToReplace.text}")
expressionToReplace, replacements.add(expressionToReplace to replacement)
psiFactory.createExpression(argumentExpression.getText() + "." + expressionToReplace.getText())
)
);
} }
else { else {
replacements.add(new Pair<KtElement, KtElement>(expressionToReplace, argumentExpression)); replacements.add(expressionToReplace to argumentExpression!!)
} }
} }
// Sort by descending offset so that call arguments are replaced before call itself // Sort by descending offset so that call arguments are replaced before call itself
ContainerUtil.sort(replacements, REVERSED_TEXT_OFFSET_COMPARATOR); ContainerUtil.sort(replacements, REVERSED_TEXT_OFFSET_COMPARATOR)
for (Pair<KtElement, KtElement> replacement : replacements) { for ((expressionToReplace, replacingExpression) in replacements) {
replacement.getFirst().replace(replacement.getSecond()); expressionToReplace.replace(replacingExpression)
} }
return newExpression; return newExpression
} }
private void updateArgumentsAndReceiver(JetChangeInfo changeInfo, KtCallElement element, @NotNull UsageInfo[] allUsages) { private fun updateArgumentsAndReceiver(changeInfo: JetChangeInfo, element: KtCallElement, allUsages: Array<out UsageInfo>) {
KtValueArgumentList arguments = element.getValueArgumentList(); var arguments = element.valueArgumentList.sure { "Argument list is expected: " + element.text }
assert arguments != null : "Argument list is expected: " + element.getText(); val oldArguments = element.valueArguments
List<? extends ValueArgument> oldArguments = element.getValueArguments();
if (isPropertyJavaUsage()) { if (isPropertyJavaUsage) return updateJavaPropertyCall(changeInfo, element)
updateJavaPropertyCall(changeInfo, element);
return;
}
boolean isNamedCall = oldArguments.size() > 1 && oldArguments.get(0).isNamed(); val isNamedCall = oldArguments.size > 1 && oldArguments[0].isNamed()
StringBuilder parametersBuilder = new StringBuilder("(");
boolean isFirst = true;
TIntArrayList indicesOfArgumentsWithDefaultValues = new TIntArrayList(); val indicesOfArgumentsWithDefaultValues = TIntArrayList()
KtPsiFactory psiFactory = new KtPsiFactory(element.getProject()); val psiFactory = KtPsiFactory(element.project)
List<JetParameterInfo> newSignatureParameters = changeInfo.getNonReceiverParameters(); val newSignatureParameters = changeInfo.getNonReceiverParameters()
for (JetParameterInfo parameterInfo : newSignatureParameters) { val newArgumentListText = newSignatureParameters
if (isFirst) { .map { parameterInfo ->
isFirst = false; val defaultValueForCall = parameterInfo.defaultValueForCall
} val defaultValueText = when {
else { element.isInsideOfCallerBody(allUsages) ->
parametersBuilder.append(','); parameterInfo.name
} defaultValueForCall != null ->
substituteReferences(defaultValueForCall, parameterInfo.defaultValueParameterReferences, psiFactory).text
else ->
""
}
val argumentValue = if (defaultValueText.isEmpty()) "0" else defaultValueText
if (isNamedCall) "${parameterInfo.getInheritedName(callee)}=$argumentValue" else argumentValue
}
.joinToString(prefix = "(", postfix = ")")
val newArgumentList = KtPsiFactory(project).createCallArguments(newArgumentListText)
KtExpression defaultValueForCall = parameterInfo.getDefaultValueForCall(); val argumentMap = getParamIndexToArgumentMap(changeInfo, oldArguments)
String defaultValueText; val newReceiverInfo = changeInfo.receiverParameterInfo
if (ChangeSignatureUtilsKt.isInsideOfCallerBody(element, allUsages)) { val originalReceiverInfo = changeInfo.methodDescriptor.receiver
defaultValueText = parameterInfo.getName();
}
else {
defaultValueText = defaultValueForCall != null
? substituteReferences(defaultValueForCall,
parameterInfo.getDefaultValueParameterReferences(),
psiFactory).getText()
: "";
}
if (isNamedCall) { val extensionReceiver = if (resolvedCall != null) resolvedCall.extensionReceiver else ReceiverValue.NO_RECEIVER
String newName = parameterInfo.getInheritedName(callee); val dispatchReceiver = if (resolvedCall != null) resolvedCall.dispatchReceiver else ReceiverValue.NO_RECEIVER
parametersBuilder.append(newName).append('=');
}
parametersBuilder.append(defaultValueText.isEmpty() ? '0' : defaultValueText); var elementToReplace: PsiElement = element
} val parent = element.parent
if (parent is KtQualifiedExpression && parent.selectorExpression == element) {
parametersBuilder.append(')'); elementToReplace = parent
KtValueArgumentList newArgumentList = KtPsiFactoryKt.KtPsiFactory(getProject()).createCallArguments(parametersBuilder.toString());
Map<Integer, ValueArgument> argumentMap = getParamIndexToArgumentMap(changeInfo, oldArguments);
JetParameterInfo newReceiverInfo = changeInfo.getReceiverParameterInfo();
JetParameterInfo originalReceiverInfo = changeInfo.getMethodDescriptor().getReceiver();
ReceiverValue extensionReceiver = resolvedCall != null ? resolvedCall.getExtensionReceiver() : ReceiverValue.NO_RECEIVER;
ReceiverValue dispatchReceiver = resolvedCall != null ? resolvedCall.getDispatchReceiver() : ReceiverValue.NO_RECEIVER;
PsiElement elementToReplace = element;
PsiElement parent = element.getParent();
if (parent instanceof KtQualifiedExpression && ((KtQualifiedExpression) parent).getSelectorExpression() == element) {
elementToReplace = parent;
} }
// Do not add extension receiver to calls with explicit dispatch receiver // Do not add extension receiver to calls with explicit dispatch receiver
if (newReceiverInfo != null if (newReceiverInfo != null && elementToReplace is KtQualifiedExpression && dispatchReceiver is ExpressionReceiver) return
&& elementToReplace instanceof KtQualifiedExpression
&& dispatchReceiver instanceof ExpressionReceiver) return;
List<KtValueArgument> newArguments = newArgumentList.getArguments(); val newArguments = newArgumentList.arguments
int actualIndex = 0; var actualIndex = 0
for (int i = 0; i < newArguments.size(); i++) { for (i in newArguments.indices) {
KtValueArgument newArgument = newArguments.get(i); val newArgument = newArguments[i]
JetParameterInfo parameterInfo = newSignatureParameters.get(i); val parameterInfo = newSignatureParameters[i]
if (parameterInfo == originalReceiverInfo) { if (parameterInfo == originalReceiverInfo) {
KtExpression receiverExpression = getReceiverExpression(extensionReceiver, psiFactory); val receiverExpression = getReceiverExpression(extensionReceiver, psiFactory)
if (receiverExpression != null) { if (receiverExpression != null) {
newArgument.replace(receiverExpression); newArgument.replace(receiverExpression)
} }
actualIndex++; actualIndex++
continue; continue
} }
ValueArgument oldArgument = argumentMap.get(parameterInfo.getOldIndex()); val oldArgument = argumentMap[parameterInfo.oldIndex]
if (oldArgument != null) { if (oldArgument != null) {
ValueArgumentName argumentName = oldArgument.getArgumentName(); val argumentName = oldArgument.getArgumentName()
KtSimpleNameExpression argumentNameExpression = argumentName != null ? argumentName.getReferenceExpression() : null; val argumentNameExpression = argumentName?.referenceExpression
changeArgumentName(argumentNameExpression, parameterInfo); changeArgumentName(argumentNameExpression, parameterInfo)
//noinspection ConstantConditions //noinspection ConstantConditions
ValueArgument argumentReplacement = (ValueArgument) newArgument.replace( val argumentReplacement = newArgument.replace(
oldArgument instanceof KtFunctionLiteralArgument if (oldArgument is KtFunctionLiteralArgument)
? psiFactory.createArgument(oldArgument.getArgumentExpression(), null, false) psiFactory.createArgument(oldArgument.getArgumentExpression(), null, false)
: oldArgument.asElement() else
); oldArgument.asElement()) as ValueArgument
argumentMap.put(parameterInfo.getOldIndex(), argumentReplacement); argumentMap.put(parameterInfo.oldIndex, argumentReplacement)
} }
// TODO: process default arguments in the middle else if (parameterInfo.defaultValueForCall == null) {
else if (parameterInfo.getDefaultValueForCall() == null) { if (parameterInfo.defaultValueForParameter != null) {
if (parameterInfo.getDefaultValueForParameter() != null) { newArgumentList.removeArgument(newArgument)
newArgumentList.removeArgument(newArgument);
} }
else { else {
newArgument.delete(); // keep space between commas newArgument.delete() // keep space between commas
} }
} }
else { else {
indicesOfArgumentsWithDefaultValues.add(actualIndex++); indicesOfArgumentsWithDefaultValues.add(actualIndex++)
} }// TODO: process default arguments in the middle
} }
List<KtFunctionLiteralArgument> lambdaArguments = element.getFunctionLiteralArguments(); val lambdaArguments = element.functionLiteralArguments
boolean hasLambdaArgumentsBefore = !lambdaArguments.isEmpty(); val hasLambdaArgumentsBefore = !lambdaArguments.isEmpty()
if (hasLambdaArgumentsBefore) { if (hasLambdaArgumentsBefore) {
element.deleteChildRange(CollectionsKt.first(lambdaArguments), CollectionsKt.last(lambdaArguments)); element.deleteChildRange(lambdaArguments.first(), lambdaArguments.last())
} }
KtValueArgument lastArgument = CollectionsKt.lastOrNull(newArgumentList.getArguments()); val lastArgument = newArgumentList.arguments.lastOrNull()
KtFunctionLiteralExpression lastLambdaExpr = val lastLambdaExpr = if (lastArgument != null) lastArgument.getArgumentExpression()?.unpackFunctionLiteral() else null
lastArgument != null ? KtFunctionLiteralArgumentKt.unpackFunctionLiteral(lastArgument.getArgumentExpression()) : null; val lastNewParam = changeInfo.newParameters.lastOrNull()
JetParameterInfo lastNewParam = ArraysKt.lastOrNull(changeInfo.getNewParameters()); val hasTrailingLambdaInArgumentListAfter =
boolean hasTrailingLambdaInArgumentListAfter = lastLambdaExpr != null && lastNewParam != null && argumentMap[lastNewParam.oldIndex] == lastArgument
lastLambdaExpr != null && lastNewParam != null && argumentMap.get(lastNewParam.getOldIndex()) == lastArgument; val newLambdaWithDefaultValueWasAdded =
boolean newLambdaWithDefaultValueWasAdded =
lastNewParam != null lastNewParam != null
&& lastNewParam.isNewParameter() && lastNewParam.isNewParameter
&& lastNewParam.getDefaultValueForCall() instanceof KtFunctionLiteralExpression && lastNewParam.defaultValueForCall is KtFunctionLiteralExpression
&& lastArgument != null && lastArgument != null
&& !lastArgument.isNamed(); && !lastArgument.isNamed()
boolean shouldMoveLambdaOut = val shouldMoveLambdaOut = hasTrailingLambdaInArgumentListAfter && hasLambdaArgumentsBefore || newLambdaWithDefaultValueWasAdded
hasTrailingLambdaInArgumentListAfter && hasLambdaArgumentsBefore || newLambdaWithDefaultValueWasAdded;
arguments = (KtValueArgumentList) arguments.replace(newArgumentList); arguments = arguments.replace(newArgumentList) as KtValueArgumentList
final List<KtElement> argumentsToShorten = new ArrayList<KtElement>(indicesOfArgumentsWithDefaultValues.size()); val argumentsToShorten = ArrayList<KtElement>(indicesOfArgumentsWithDefaultValues.size())
final List<KtValueArgument> argumentList = arguments.getArguments(); val argumentList = arguments.arguments
indicesOfArgumentsWithDefaultValues.forEach( indicesOfArgumentsWithDefaultValues.forEach {
new TIntProcedure() { argumentsToShorten.add(argumentList[it])
@Override true
public boolean execute(int i) {
argumentsToShorten.add(argumentList.get(i));
return true;
}
}
);
for (KtElement argument : argumentsToShorten) {
ShortenWaitingSetKt.addToShorteningWaitSet(argument, SHORTEN_ARGUMENTS_OPTIONS);
} }
argumentsToShorten.forEach { it.addToShorteningWaitSet(SHORTEN_ARGUMENTS_OPTIONS) }
KtElement newElement = element; var newElement: KtElement = element
if (newReceiverInfo != originalReceiverInfo) { if (newReceiverInfo != originalReceiverInfo) {
PsiElement replacingElement; val replacingElement: PsiElement
if (newReceiverInfo != null) { if (newReceiverInfo != null) {
ValueArgument receiverArgument = argumentMap.get(newReceiverInfo.getOldIndex()); val receiverArgument = argumentMap[newReceiverInfo.oldIndex]
KtExpression extensionReceiverExpression = receiverArgument != null ? receiverArgument.getArgumentExpression() : null; val extensionReceiverExpression = receiverArgument?.getArgumentExpression()
KtExpression defaultValueForCall = newReceiverInfo.getDefaultValueForCall(); val defaultValueForCall = newReceiverInfo.defaultValueForCall
KtExpression receiver = val receiver = extensionReceiverExpression?.let { psiFactory.createExpression(it.text) }
extensionReceiverExpression != null ? psiFactory.createExpression(extensionReceiverExpression.getText()) ?: defaultValueForCall
: defaultValueForCall != null ? defaultValueForCall ?: psiFactory.createExpression("_")
: psiFactory.createExpression("_");
replacingElement = CreateByPatternKt.createExpressionByPattern(psiFactory, "$0.$1", receiver, element); replacingElement = psiFactory.createExpressionByPattern("$0.$1", receiver, element)
} }
else { else {
replacingElement = psiFactory.createExpression(element.getText()); replacingElement = psiFactory.createExpression(element.text)
} }
newElement = (KtElement) elementToReplace.replace(replacingElement); newElement = elementToReplace.replace(replacingElement) as KtElement
} }
if (shouldMoveLambdaOut) { if (shouldMoveLambdaOut) {
KtCallExpression newCallExpression = val newCallExpression = ((newElement as? KtQualifiedExpression)?.selectorExpression ?: newElement) as KtCallExpression
(KtCallExpression) (newElement instanceof KtQualifiedExpression newCallExpression.moveFunctionLiteralOutsideParentheses()
? ((KtQualifiedExpression) newElement).getSelectorExpression()
: newElement);
PsiModificationUtilsKt.moveFunctionLiteralOutsideParentheses(newCallExpression);
} }
} }
private static void updateJavaPropertyCall(JetChangeInfo changeInfo, KtCallElement element) { private fun changeArgumentNames(changeInfo: JetChangeInfo, element: KtCallElement) {
JetParameterInfo newReceiverInfo = changeInfo.getReceiverParameterInfo(); for (argument in element.valueArguments) {
JetParameterInfo originalReceiverInfo = changeInfo.getMethodDescriptor().getReceiver(); val argumentName = argument.getArgumentName()
if (newReceiverInfo == originalReceiverInfo) return; val argumentNameExpression = argumentName?.referenceExpression ?: continue
val oldParameterIndex = changeInfo.getOldParameterIndex(argumentNameExpression.getReferencedName()) ?: continue
KtValueArgumentList arguments = element.getValueArgumentList(); val newParameterIndex = if (changeInfo.receiverParameterInfo != null) oldParameterIndex + 1 else oldParameterIndex
assert arguments != null : "Argument list is expected: " + element.getText(); val parameterInfo = changeInfo.newParameters[newParameterIndex]
List<? extends ValueArgument> oldArguments = element.getValueArguments(); changeArgumentName(argumentNameExpression, parameterInfo)
KtPsiFactory psiFactory = new KtPsiFactory(element.getProject());
KtValueArgument firstArgument = oldArguments.isEmpty() ? null : (KtValueArgument) oldArguments.get(0);
if (newReceiverInfo == null) {
if (firstArgument != null) arguments.removeArgument(firstArgument);
}
else {
KtExpression defaultValueForCall = newReceiverInfo.getDefaultValueForCall();
if (defaultValueForCall == null) {
defaultValueForCall = psiFactory.createExpression("_");
}
KtValueArgument newReceiverArgument = psiFactory.createArgument(defaultValueForCall, null, false);
if (originalReceiverInfo != null) {
if (firstArgument != null) firstArgument.replace(newReceiverArgument);
}
else {
arguments.addArgumentAfter(newReceiverArgument, null);
}
} }
} }
@Nullable private fun changeArgumentName(argumentNameExpression: KtSimpleNameExpression?, parameterInfo: JetParameterInfo) {
private static KtExpression getReceiverExpression(@NotNull ReceiverValue receiver, @NotNull KtPsiFactory psiFactory) { val identifier = argumentNameExpression?.getIdentifier() ?: return
if (receiver instanceof ExpressionReceiver) { val newName = parameterInfo.getInheritedName(callee)
return ((ExpressionReceiver) receiver).getExpression(); identifier.replace(KtPsiFactory(project).createIdentifier(newName))
}
else if (receiver instanceof ThisReceiver) {
DeclarationDescriptor descriptor = ((ThisReceiver) receiver).getDeclarationDescriptor();
String thisText = descriptor instanceof ClassDescriptor ? "this@" + descriptor.getName().asString() : "this";
return psiFactory.createExpression(thisText);
}
return null;
} }
private static Map<Integer, ValueArgument> getParamIndexToArgumentMap(JetChangeInfo changeInfo, List<? extends ValueArgument> oldArguments) { companion object {
Map<Integer, ValueArgument> argumentMap = new HashMap<Integer, ValueArgument>(); private val REVERSED_TEXT_OFFSET_COMPARATOR = object : Comparator<Pair<KtElement, KtElement>> {
override fun compare(p1: Pair<KtElement, KtElement>, p2: Pair<KtElement, KtElement>): Int {
for (int i = 0; i < oldArguments.size(); i++) { val offset1 = p1.first.startOffset
ValueArgument argument = oldArguments.get(i); val offset2 = p2.first.startOffset
ValueArgumentName argumentName = argument.getArgumentName(); return when {
String oldParameterName = argumentName != null ? argumentName.getAsName().asString() : null; offset1 < offset2 -> 1
offset1 > offset2 -> -1
if (oldParameterName != null) { else -> 0
Integer oldParameterIndex = changeInfo.getOldParameterIndex(oldParameterName); }
if (oldParameterIndex != null)
argumentMap.put(oldParameterIndex, argument);
}
else
argumentMap.put(i, argument);
}
return argumentMap;
}
private void changeArgumentNames(JetChangeInfo changeInfo, KtCallElement element) {
for (ValueArgument argument : element.getValueArguments()) {
ValueArgumentName argumentName = argument.getArgumentName();
KtSimpleNameExpression argumentNameExpression = argumentName != null ? argumentName.getReferenceExpression() : null;
if (argumentNameExpression != null) {
Integer oldParameterIndex = changeInfo.getOldParameterIndex(argumentNameExpression.getReferencedName());
if (oldParameterIndex == null) continue;
JetParameterInfo parameterInfo = changeInfo.getNewParameters()[
changeInfo.getReceiverParameterInfo() != null ? oldParameterIndex + 1 : oldParameterIndex
];
changeArgumentName(argumentNameExpression, parameterInfo);
} }
} }
}
private void changeArgumentName(KtSimpleNameExpression argumentNameExpression, JetParameterInfo parameterInfo) { private val SHORTEN_ARGUMENTS_OPTIONS = ShortenReferences.Options(true, true)
PsiElement identifier = argumentNameExpression != null ? argumentNameExpression.getIdentifier() : null;
if (identifier != null) { private fun updateJavaPropertyCall(changeInfo: JetChangeInfo, element: KtCallElement) {
String newName = parameterInfo.getInheritedName(callee); val newReceiverInfo = changeInfo.receiverParameterInfo
identifier.replace(KtPsiFactoryKt.KtPsiFactory(getProject()).createIdentifier(newName)); val originalReceiverInfo = changeInfo.methodDescriptor.receiver
if (newReceiverInfo == originalReceiverInfo) return
val arguments = element.valueArgumentList.sure { "Argument list is expected: " + element.text }
val oldArguments = element.valueArguments
val psiFactory = KtPsiFactory(element.project)
val firstArgument = oldArguments.firstOrNull() as KtValueArgument?
when {
newReceiverInfo != null -> {
val defaultValueForCall = newReceiverInfo.defaultValueForCall ?: psiFactory.createExpression("_")
val newReceiverArgument = psiFactory.createArgument(defaultValueForCall, null, false)
if (originalReceiverInfo != null) {
firstArgument?.replace(newReceiverArgument)
}
else {
arguments.addArgumentAfter(newReceiverArgument, null)
}
}
firstArgument != null -> arguments.removeArgument(firstArgument)
}
}
private fun getReceiverExpression(receiver: ReceiverValue, psiFactory: KtPsiFactory): KtExpression? {
return when (receiver) {
is ExpressionReceiver -> receiver.expression
is ThisReceiver -> {
val descriptor = receiver.declarationDescriptor
val thisText = if (descriptor is ClassDescriptor) "this@" + descriptor.name.asString() else "this"
psiFactory.createExpression(thisText)
}
else -> null
}
}
private fun getParamIndexToArgumentMap(changeInfo: JetChangeInfo, oldArguments: List<ValueArgument>): MutableMap<Int, ValueArgument> {
val argumentMap = HashMap<Int, ValueArgument>()
for (i in oldArguments.indices) {
val argument = oldArguments[i]
val argumentName = argument.getArgumentName()
val oldParameterName = if (argumentName != null) argumentName.asName.asString() else null
if (oldParameterName != null) {
val oldParameterIndex = changeInfo.getOldParameterIndex(oldParameterName)
if (oldParameterIndex != null)
argumentMap.put(oldParameterIndex, argument)
}
else
argumentMap.put(i, argument)
}
return argumentMap
} }
} }
} }