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.
*/
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.PsiReference;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntProcedure;
import kotlin.ArraysKt;
import kotlin.CollectionsKt;
import kotlin.Pair;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
import org.jetbrains.kotlin.idea.codeInsight.shorten.ShortenWaitingSetKt;
import org.jetbrains.kotlin.idea.core.PsiModificationUtilsKt;
import org.jetbrains.kotlin.idea.refactoring.changeSignature.ChangeSignatureUtilsKt;
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo;
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo;
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.ExtractorUtilKt;
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler;
import org.jetbrains.kotlin.idea.util.ShortenReferences;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.*;
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.KotlinType;
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
import org.jetbrains.kotlin.types.expressions.OperatorConventions;
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.usageView.UsageInfo
import com.intellij.util.containers.ContainerUtil
import gnu.trove.TIntArrayList
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
import org.jetbrains.kotlin.idea.core.moveFunctionLiteralOutsideParentheses
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo
import org.jetbrains.kotlin.idea.refactoring.changeSignature.isInsideOfCallerBody
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.createNameCounterpartMap
import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinIntroduceVariableHandler
import org.jetbrains.kotlin.idea.util.ShortenReferences
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
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.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.utils.sure
import java.util.*
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> {
private static final Comparator<Pair<KtElement, KtElement>>
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;
}
};
override fun processUsage(changeInfo: JetChangeInfo, element: KtCallElement, allUsages: Array<out UsageInfo>): Boolean {
if (shouldSkipUsage(element)) return true
private static final ShortenReferences.Options SHORTEN_ARGUMENTS_OPTIONS = new ShortenReferences.Options(true, true);
changeNameIfNeeded(changeInfo, element)
private final JetCallableDefinitionUsage<?> callee;
private final BindingContext context;
private final ResolvedCall<? extends CallableDescriptor> resolvedCall;
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);
if (element.valueArgumentList != null) {
if (changeInfo.isParameterSetOrOrderChanged) {
updateArgumentsAndReceiver(changeInfo, element, allUsages)
}
else {
changeArgumentNames(changeInfo, element);
changeArgumentNames(changeInfo, element)
}
}
if (changeInfo.getNewParametersCount() == 0
&& element instanceof KtDelegatorToSuperCall) {
KtEnumEntry enumEntry = PsiTreeUtil.getParentOfType(element, KtEnumEntry.class, true);
if (enumEntry != null && enumEntry.getInitializerList() == element.getParent()) {
PsiElement colon = enumEntry.getColon();
KtInitializerList initializerList = enumEntry.getInitializerList();
enumEntry.deleteChildRange(colon != null ? colon : initializerList, initializerList);
if (changeInfo.getNewParametersCount() == 0 && element is KtDelegatorToSuperCall) {
val enumEntry = element.getStrictParentOfType<KtEnumEntry>()
if (enumEntry != null && enumEntry.initializerList == element.parent) {
val initializerList = enumEntry.initializerList
enumEntry.deleteChildRange(enumEntry.getColon() ?: 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
if (resolvedCall == null && !(element instanceof KtDelegatorToSuperCall)) return true;
if (resolvedCall != null && !ArgumentMappingKt.isReallySuccess(resolvedCall)) {
// TODO: investigate why arguments are not recorded for enum constructor call
if (element instanceof KtDelegatorToSuperCall && element.getParent().getParent() instanceof KtEnumEntry) return false;
for (ValueArgument valueArgument : resolvedCall.getCall().getValueArguments()) {
if (!(resolvedCall.getArgumentMapping(valueArgument) instanceof ArgumentMatch)) return true;
}
Map<ValueParameterDescriptor, ResolvedValueArgument> arguments = resolvedCall.getValueArguments();
for (ValueParameterDescriptor valueParameter : resolvedCall.getResultingDescriptor().getValueParameters()) {
if (!arguments.containsKey(valueParameter)) return true;
}
}
return false;
if (resolvedCall == null && element !is KtDelegatorToSuperCall) return true
if (resolvedCall == null || resolvedCall.isReallySuccess()) return false
// TODO: investigate why arguments are not recorded for enum constructor call
if (element is KtDelegatorToSuperCall && element.parent.parent is KtEnumEntry) return false
if (!resolvedCall.call.valueArguments.all{ resolvedCall.getArgumentMapping(it) is ArgumentMatch }) return true
val arguments = resolvedCall.valueArguments
return !resolvedCall.resultingDescriptor.valueParameters.all{ arguments.containsKey(it) }
}
private boolean isPropertyJavaUsage() {
PsiElement calleeElement = this.callee.getElement();
return (calleeElement instanceof KtProperty || calleeElement instanceof KtParameter)
&& resolvedCall != null && resolvedCall.getResultingDescriptor() instanceof 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);
private val isPropertyJavaUsage: Boolean
get() {
val calleeElement = this.callee.element
if (calleeElement !is KtProperty && calleeElement !is KtParameter) return false
return resolvedCall?.resultingDescriptor is JavaMethodDescriptor
}
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 KtExpression getReceiverExpressionIfMatched(
@NotNull ReceiverValue receiverValue,
@NotNull DeclarationDescriptor originalDescriptor,
@NotNull KtPsiFactory psiFactory
) {
if (!receiverValue.exists()) return null;
private fun getReceiverExpressionIfMatched(
receiverValue: ReceiverValue,
originalDescriptor: DeclarationDescriptor,
psiFactory: KtPsiFactory
): KtExpression? {
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;
val adjustedDescriptor = if (originalDescriptor is CallableDescriptor && originalDescriptor !is ReceiverParameterDescriptor) {
originalDescriptor.extensionReceiverParameter ?: return null
}
else originalDescriptor
boolean currentIsExtension = resolvedCall.getExtensionReceiver() == receiverValue;
boolean originalIsExtension =
originalDescriptor instanceof ReceiverParameterDescriptor &&
((ReceiverParameterDescriptor) originalDescriptor).getValue() instanceof ExtensionReceiver;
if (currentIsExtension != originalIsExtension) return null;
val currentIsExtension = resolvedCall!!.extensionReceiver == receiverValue
val originalIsExtension = adjustedDescriptor is ReceiverParameterDescriptor && adjustedDescriptor.value is ExtensionReceiver
if (currentIsExtension != originalIsExtension) return null
KotlinType originalType = originalDescriptor instanceof ReceiverParameterDescriptor
? ((ReceiverParameterDescriptor) originalDescriptor).getType()
: originalDescriptor instanceof ClassDescriptor
? ((ClassDescriptor) originalDescriptor).getDefaultType()
: null;
if (originalType == null || !KotlinTypeChecker.DEFAULT.isSubtypeOf(receiverValue.getType(), originalType)) return null;
val originalType = when (adjustedDescriptor) {
is ReceiverParameterDescriptor -> adjustedDescriptor.type
is ClassDescriptor -> adjustedDescriptor.defaultType
else -> 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) {
if (element instanceof KtConstantExpression) return false;
if (element instanceof KtThisExpression) return false;
if (element instanceof KtSimpleNameExpression) return false;
//noinspection SuspiciousMethodCalls
if (element instanceof KtBinaryExpression
&& 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;
private fun needSeparateVariable(element: PsiElement): Boolean {
return when {
element is KtConstantExpression, element is KtThisExpression, element is KtSimpleNameExpression -> false
element is KtBinaryExpression && OperatorConventions.ASSIGNMENT_OPERATIONS.containsKeyRaw(element.operationToken) -> true
element is KtUnaryExpression && OperatorConventions.INCREMENT_OPERATIONS.containsRaw(element.operationToken) -> true
element is KtCallExpression -> element.getResolvedCall(context)?.resultingDescriptor is ConstructorDescriptor
else -> element.children.any { needSeparateVariable(it) }
}
for (PsiElement child : element.getChildren()) {
if (needSeparateVariable(child)) return true;
}
return false;
}
@NotNull
private KtExpression substituteReferences(
@NotNull KtExpression expression,
@NotNull Map<PsiReference, DeclarationDescriptor> referenceMap,
@NotNull final KtPsiFactory psiFactory
) {
if (referenceMap.isEmpty() || resolvedCall == null) return expression;
private fun substituteReferences(
expression: KtExpression,
referenceMap: Map<PsiReference, DeclarationDescriptor>,
psiFactory: KtPsiFactory
): KtExpression {
if (referenceMap.isEmpty() || resolvedCall == null) return expression
KtExpression newExpression = (KtExpression) expression.copy();
val newExpression = expression.copy() as KtExpression
Map<KtSimpleNameExpression, KtSimpleNameExpression> nameCounterpartMap =
ExtractorUtilKt.createNameCounterpartMap(expression, newExpression);
val nameCounterpartMap = createNameCounterpartMap(expression, newExpression)
Map<ValueParameterDescriptor, ResolvedValueArgument> valueArguments = resolvedCall.getValueArguments();
val valueArguments = resolvedCall.valueArguments
List<Pair<KtElement, KtElement>> replacements = new ArrayList<Pair<KtElement, KtElement>>();
for (Map.Entry<PsiReference, DeclarationDescriptor> e : referenceMap.entrySet()) {
DeclarationDescriptor descriptor = e.getValue();
KtExpression argumentExpression;
boolean addReceiver = false;
if (descriptor instanceof ValueParameterDescriptor) { // Ordinary parameter
val replacements = ArrayList<Pair<KtElement, KtElement>>()
loop@ for ((ref, descriptor) in referenceMap.entries) {
var argumentExpression: KtExpression?
val addReceiver: Boolean
if (descriptor is 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());
val parameterDescriptor = resolvedCall.resultingDescriptor.valueParameters[descriptor.index]
val resolvedValueArgument = valueArguments[parameterDescriptor] as? ExpressionValueArgument ?: continue
val argument = resolvedValueArgument.valueArgument ?: continue
ResolvedValueArgument resolvedValueArgument = valueArguments.get(parameterDescriptor);
if (!(resolvedValueArgument instanceof ExpressionValueArgument)) continue;
ValueArgument argument = ((ExpressionValueArgument) resolvedValueArgument).getValueArgument();
if (argument == null) continue;
argumentExpression = argument.getArgumentExpression();
addReceiver = false
argumentExpression = argument.getArgumentExpression()
}
else {
addReceiver = !(descriptor instanceof ReceiverParameterDescriptor);
argumentExpression = getReceiverExpressionIfMatched(resolvedCall.getExtensionReceiver(), descriptor, psiFactory);
if (argumentExpression == null) {
argumentExpression = getReceiverExpressionIfMatched(resolvedCall.getDispatchReceiver(), descriptor, psiFactory);
addReceiver = descriptor !is ReceiverParameterDescriptor
argumentExpression = getReceiverExpressionIfMatched(resolvedCall.extensionReceiver, descriptor, psiFactory)
?: getReceiverExpressionIfMatched(resolvedCall.dispatchReceiver, 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
if (needSeparateVariable(argumentExpression) &&
PsiTreeUtil.getNonStrictParentOfType(getElement(),
KtConstructorDelegationCall.class,
KtDelegationSpecifier.class,
KtParameter.class) == null) {
var expressionToReplace: KtExpression = nameCounterpartMap.getRaw(ref.element) ?: continue
val parent = expressionToReplace.parent
final Ref<KtExpression> newExpressionRef = new Ref<KtExpression>();
KotlinIntroduceVariableHandler.doRefactoring(
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 (parent is KtThisExpression) {
expressionToReplace = parent
}
if (addReceiver) {
KtCallExpression callExpression = PsiTreeUtil.getParentOfType(expressionToReplace, KtCallExpression.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 KtOperationExpression
&& ((KtOperationExpression) parent).getOperationReference() == expressionToReplace) {
continue;
}
val callExpression = expressionToReplace.getParentOfTypeAndBranch<KtCallExpression>(true) { calleeExpression }
when {
callExpression != null -> expressionToReplace = callExpression
parent is KtOperationExpression && parent.operationReference == expressionToReplace -> continue@loop
}
replacements.add(
new Pair<KtElement, KtElement>(
expressionToReplace,
psiFactory.createExpression(argumentExpression.getText() + "." + expressionToReplace.getText())
)
);
val replacement = psiFactory.createExpression("${argumentExpression!!.text}.${expressionToReplace.text}")
replacements.add(expressionToReplace to replacement)
}
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
ContainerUtil.sort(replacements, REVERSED_TEXT_OFFSET_COMPARATOR);
for (Pair<KtElement, KtElement> replacement : replacements) {
replacement.getFirst().replace(replacement.getSecond());
ContainerUtil.sort(replacements, REVERSED_TEXT_OFFSET_COMPARATOR)
for ((expressionToReplace, replacingExpression) in replacements) {
expressionToReplace.replace(replacingExpression)
}
return newExpression;
return newExpression
}
private void updateArgumentsAndReceiver(JetChangeInfo changeInfo, KtCallElement element, @NotNull UsageInfo[] allUsages) {
KtValueArgumentList arguments = element.getValueArgumentList();
assert arguments != null : "Argument list is expected: " + element.getText();
List<? extends ValueArgument> oldArguments = element.getValueArguments();
private fun updateArgumentsAndReceiver(changeInfo: JetChangeInfo, element: KtCallElement, allUsages: Array<out UsageInfo>) {
var arguments = element.valueArgumentList.sure { "Argument list is expected: " + element.text }
val oldArguments = element.valueArguments
if (isPropertyJavaUsage()) {
updateJavaPropertyCall(changeInfo, element);
return;
}
if (isPropertyJavaUsage) return updateJavaPropertyCall(changeInfo, element)
boolean isNamedCall = oldArguments.size() > 1 && oldArguments.get(0).isNamed();
StringBuilder parametersBuilder = new StringBuilder("(");
boolean isFirst = true;
val isNamedCall = oldArguments.size > 1 && oldArguments[0].isNamed()
TIntArrayList indicesOfArgumentsWithDefaultValues = new TIntArrayList();
val indicesOfArgumentsWithDefaultValues = TIntArrayList()
KtPsiFactory psiFactory = new KtPsiFactory(element.getProject());
val psiFactory = KtPsiFactory(element.project)
List<JetParameterInfo> newSignatureParameters = changeInfo.getNonReceiverParameters();
for (JetParameterInfo parameterInfo : newSignatureParameters) {
if (isFirst) {
isFirst = false;
}
else {
parametersBuilder.append(',');
}
val newSignatureParameters = changeInfo.getNonReceiverParameters()
val newArgumentListText = newSignatureParameters
.map { parameterInfo ->
val defaultValueForCall = parameterInfo.defaultValueForCall
val defaultValueText = when {
element.isInsideOfCallerBody(allUsages) ->
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;
if (ChangeSignatureUtilsKt.isInsideOfCallerBody(element, allUsages)) {
defaultValueText = parameterInfo.getName();
}
else {
defaultValueText = defaultValueForCall != null
? substituteReferences(defaultValueForCall,
parameterInfo.getDefaultValueParameterReferences(),
psiFactory).getText()
: "";
}
val newReceiverInfo = changeInfo.receiverParameterInfo
val originalReceiverInfo = changeInfo.methodDescriptor.receiver
if (isNamedCall) {
String newName = parameterInfo.getInheritedName(callee);
parametersBuilder.append(newName).append('=');
}
val extensionReceiver = if (resolvedCall != null) resolvedCall.extensionReceiver else ReceiverValue.NO_RECEIVER
val dispatchReceiver = if (resolvedCall != null) resolvedCall.dispatchReceiver else ReceiverValue.NO_RECEIVER
parametersBuilder.append(defaultValueText.isEmpty() ? '0' : defaultValueText);
}
parametersBuilder.append(')');
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;
var elementToReplace: PsiElement = element
val parent = element.parent
if (parent is KtQualifiedExpression && parent.selectorExpression == element) {
elementToReplace = parent
}
// Do not add extension receiver to calls with explicit dispatch receiver
if (newReceiverInfo != null
&& elementToReplace instanceof KtQualifiedExpression
&& dispatchReceiver instanceof ExpressionReceiver) return;
if (newReceiverInfo != null && elementToReplace is KtQualifiedExpression && dispatchReceiver is ExpressionReceiver) return
List<KtValueArgument> newArguments = newArgumentList.getArguments();
int actualIndex = 0;
for (int i = 0; i < newArguments.size(); i++) {
KtValueArgument newArgument = newArguments.get(i);
JetParameterInfo parameterInfo = newSignatureParameters.get(i);
val newArguments = newArgumentList.arguments
var actualIndex = 0
for (i in newArguments.indices) {
val newArgument = newArguments[i]
val parameterInfo = newSignatureParameters[i]
if (parameterInfo == originalReceiverInfo) {
KtExpression receiverExpression = getReceiverExpression(extensionReceiver, psiFactory);
val receiverExpression = getReceiverExpression(extensionReceiver, psiFactory)
if (receiverExpression != null) {
newArgument.replace(receiverExpression);
newArgument.replace(receiverExpression)
}
actualIndex++;
continue;
actualIndex++
continue
}
ValueArgument oldArgument = argumentMap.get(parameterInfo.getOldIndex());
val oldArgument = argumentMap[parameterInfo.oldIndex]
if (oldArgument != null) {
ValueArgumentName argumentName = oldArgument.getArgumentName();
KtSimpleNameExpression argumentNameExpression = argumentName != null ? argumentName.getReferenceExpression() : null;
changeArgumentName(argumentNameExpression, parameterInfo);
val argumentName = oldArgument.getArgumentName()
val argumentNameExpression = argumentName?.referenceExpression
changeArgumentName(argumentNameExpression, parameterInfo)
//noinspection ConstantConditions
ValueArgument argumentReplacement = (ValueArgument) newArgument.replace(
oldArgument instanceof KtFunctionLiteralArgument
? psiFactory.createArgument(oldArgument.getArgumentExpression(), null, false)
: oldArgument.asElement()
);
argumentMap.put(parameterInfo.getOldIndex(), argumentReplacement);
val argumentReplacement = newArgument.replace(
if (oldArgument is KtFunctionLiteralArgument)
psiFactory.createArgument(oldArgument.getArgumentExpression(), null, false)
else
oldArgument.asElement()) as ValueArgument
argumentMap.put(parameterInfo.oldIndex, argumentReplacement)
}
// TODO: process default arguments in the middle
else if (parameterInfo.getDefaultValueForCall() == null) {
if (parameterInfo.getDefaultValueForParameter() != null) {
newArgumentList.removeArgument(newArgument);
else if (parameterInfo.defaultValueForCall == null) {
if (parameterInfo.defaultValueForParameter != null) {
newArgumentList.removeArgument(newArgument)
}
else {
newArgument.delete(); // keep space between commas
newArgument.delete() // keep space between commas
}
}
else {
indicesOfArgumentsWithDefaultValues.add(actualIndex++);
}
indicesOfArgumentsWithDefaultValues.add(actualIndex++)
}// TODO: process default arguments in the middle
}
List<KtFunctionLiteralArgument> lambdaArguments = element.getFunctionLiteralArguments();
boolean hasLambdaArgumentsBefore = !lambdaArguments.isEmpty();
val lambdaArguments = element.functionLiteralArguments
val hasLambdaArgumentsBefore = !lambdaArguments.isEmpty()
if (hasLambdaArgumentsBefore) {
element.deleteChildRange(CollectionsKt.first(lambdaArguments), CollectionsKt.last(lambdaArguments));
element.deleteChildRange(lambdaArguments.first(), lambdaArguments.last())
}
KtValueArgument lastArgument = CollectionsKt.lastOrNull(newArgumentList.getArguments());
KtFunctionLiteralExpression lastLambdaExpr =
lastArgument != null ? KtFunctionLiteralArgumentKt.unpackFunctionLiteral(lastArgument.getArgumentExpression()) : null;
JetParameterInfo lastNewParam = ArraysKt.lastOrNull(changeInfo.getNewParameters());
boolean hasTrailingLambdaInArgumentListAfter =
lastLambdaExpr != null && lastNewParam != null && argumentMap.get(lastNewParam.getOldIndex()) == lastArgument;
boolean newLambdaWithDefaultValueWasAdded =
val lastArgument = newArgumentList.arguments.lastOrNull()
val lastLambdaExpr = if (lastArgument != null) lastArgument.getArgumentExpression()?.unpackFunctionLiteral() else null
val lastNewParam = changeInfo.newParameters.lastOrNull()
val hasTrailingLambdaInArgumentListAfter =
lastLambdaExpr != null && lastNewParam != null && argumentMap[lastNewParam.oldIndex] == lastArgument
val newLambdaWithDefaultValueWasAdded =
lastNewParam != null
&& lastNewParam.isNewParameter()
&& lastNewParam.getDefaultValueForCall() instanceof KtFunctionLiteralExpression
&& lastNewParam.isNewParameter
&& lastNewParam.defaultValueForCall is KtFunctionLiteralExpression
&& lastArgument != null
&& !lastArgument.isNamed();
boolean shouldMoveLambdaOut =
hasTrailingLambdaInArgumentListAfter && hasLambdaArgumentsBefore || newLambdaWithDefaultValueWasAdded;
&& !lastArgument.isNamed()
val shouldMoveLambdaOut = hasTrailingLambdaInArgumentListAfter && hasLambdaArgumentsBefore || newLambdaWithDefaultValueWasAdded
arguments = (KtValueArgumentList) arguments.replace(newArgumentList);
arguments = arguments.replace(newArgumentList) as KtValueArgumentList
final List<KtElement> argumentsToShorten = new ArrayList<KtElement>(indicesOfArgumentsWithDefaultValues.size());
final List<KtValueArgument> argumentList = arguments.getArguments();
indicesOfArgumentsWithDefaultValues.forEach(
new TIntProcedure() {
@Override
public boolean execute(int i) {
argumentsToShorten.add(argumentList.get(i));
return true;
}
}
);
for (KtElement argument : argumentsToShorten) {
ShortenWaitingSetKt.addToShorteningWaitSet(argument, SHORTEN_ARGUMENTS_OPTIONS);
val argumentsToShorten = ArrayList<KtElement>(indicesOfArgumentsWithDefaultValues.size())
val argumentList = arguments.arguments
indicesOfArgumentsWithDefaultValues.forEach {
argumentsToShorten.add(argumentList[it])
true
}
argumentsToShorten.forEach { it.addToShorteningWaitSet(SHORTEN_ARGUMENTS_OPTIONS) }
KtElement newElement = element;
var newElement: KtElement = element
if (newReceiverInfo != originalReceiverInfo) {
PsiElement replacingElement;
val replacingElement: PsiElement
if (newReceiverInfo != null) {
ValueArgument receiverArgument = argumentMap.get(newReceiverInfo.getOldIndex());
KtExpression extensionReceiverExpression = receiverArgument != null ? receiverArgument.getArgumentExpression() : null;
KtExpression defaultValueForCall = newReceiverInfo.getDefaultValueForCall();
KtExpression receiver =
extensionReceiverExpression != null ? psiFactory.createExpression(extensionReceiverExpression.getText())
: defaultValueForCall != null ? defaultValueForCall
: psiFactory.createExpression("_");
val receiverArgument = argumentMap[newReceiverInfo.oldIndex]
val extensionReceiverExpression = receiverArgument?.getArgumentExpression()
val defaultValueForCall = newReceiverInfo.defaultValueForCall
val receiver = extensionReceiverExpression?.let { psiFactory.createExpression(it.text) }
?: defaultValueForCall
?: psiFactory.createExpression("_")
replacingElement = CreateByPatternKt.createExpressionByPattern(psiFactory, "$0.$1", receiver, element);
replacingElement = psiFactory.createExpressionByPattern("$0.$1", receiver, element)
}
else {
replacingElement = psiFactory.createExpression(element.getText());
replacingElement = psiFactory.createExpression(element.text)
}
newElement = (KtElement) elementToReplace.replace(replacingElement);
newElement = elementToReplace.replace(replacingElement) as KtElement
}
if (shouldMoveLambdaOut) {
KtCallExpression newCallExpression =
(KtCallExpression) (newElement instanceof KtQualifiedExpression
? ((KtQualifiedExpression) newElement).getSelectorExpression()
: newElement);
PsiModificationUtilsKt.moveFunctionLiteralOutsideParentheses(newCallExpression);
val newCallExpression = ((newElement as? KtQualifiedExpression)?.selectorExpression ?: newElement) as KtCallExpression
newCallExpression.moveFunctionLiteralOutsideParentheses()
}
}
private static void updateJavaPropertyCall(JetChangeInfo changeInfo, KtCallElement element) {
JetParameterInfo newReceiverInfo = changeInfo.getReceiverParameterInfo();
JetParameterInfo originalReceiverInfo = changeInfo.getMethodDescriptor().getReceiver();
if (newReceiverInfo == originalReceiverInfo) return;
KtValueArgumentList arguments = element.getValueArgumentList();
assert arguments != null : "Argument list is expected: " + element.getText();
List<? extends ValueArgument> oldArguments = element.getValueArguments();
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);
}
private fun changeArgumentNames(changeInfo: JetChangeInfo, element: KtCallElement) {
for (argument in element.valueArguments) {
val argumentName = argument.getArgumentName()
val argumentNameExpression = argumentName?.referenceExpression ?: continue
val oldParameterIndex = changeInfo.getOldParameterIndex(argumentNameExpression.getReferencedName()) ?: continue
val newParameterIndex = if (changeInfo.receiverParameterInfo != null) oldParameterIndex + 1 else oldParameterIndex
val parameterInfo = changeInfo.newParameters[newParameterIndex]
changeArgumentName(argumentNameExpression, parameterInfo)
}
}
@Nullable
private static KtExpression getReceiverExpression(@NotNull ReceiverValue receiver, @NotNull KtPsiFactory psiFactory) {
if (receiver instanceof ExpressionReceiver) {
return ((ExpressionReceiver) receiver).getExpression();
}
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 fun changeArgumentName(argumentNameExpression: KtSimpleNameExpression?, parameterInfo: JetParameterInfo) {
val identifier = argumentNameExpression?.getIdentifier() ?: return
val newName = parameterInfo.getInheritedName(callee)
identifier.replace(KtPsiFactory(project).createIdentifier(newName))
}
private static Map<Integer, ValueArgument> getParamIndexToArgumentMap(JetChangeInfo changeInfo, List<? extends ValueArgument> oldArguments) {
Map<Integer, ValueArgument> argumentMap = new HashMap<Integer, ValueArgument>();
for (int i = 0; i < oldArguments.size(); i++) {
ValueArgument argument = oldArguments.get(i);
ValueArgumentName argumentName = argument.getArgumentName();
String oldParameterName = argumentName != null ? argumentName.getAsName().asString() : null;
if (oldParameterName != null) {
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);
companion object {
private val REVERSED_TEXT_OFFSET_COMPARATOR = object : Comparator<Pair<KtElement, KtElement>> {
override fun compare(p1: Pair<KtElement, KtElement>, p2: Pair<KtElement, KtElement>): Int {
val offset1 = p1.first.startOffset
val offset2 = p2.first.startOffset
return when {
offset1 < offset2 -> 1
offset1 > offset2 -> -1
else -> 0
}
}
}
}
private void changeArgumentName(KtSimpleNameExpression argumentNameExpression, JetParameterInfo parameterInfo) {
PsiElement identifier = argumentNameExpression != null ? argumentNameExpression.getIdentifier() : null;
private val SHORTEN_ARGUMENTS_OPTIONS = ShortenReferences.Options(true, true)
if (identifier != null) {
String newName = parameterInfo.getInheritedName(callee);
identifier.replace(KtPsiFactoryKt.KtPsiFactory(getProject()).createIdentifier(newName));
private fun updateJavaPropertyCall(changeInfo: JetChangeInfo, element: KtCallElement) {
val newReceiverInfo = changeInfo.receiverParameterInfo
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
}
}
}