diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeInfo.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeInfo.kt index 4aa9fd89aa3..d0fd4378a7d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeInfo.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeInfo.kt @@ -19,13 +19,14 @@ package org.jetbrains.kotlin.idea.refactoring.changeSignature import com.intellij.lang.Language import com.intellij.lang.java.JavaLanguage import com.intellij.psi.* -import com.intellij.refactoring.changeSignature.ChangeInfo -import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor -import com.intellij.refactoring.changeSignature.JavaChangeInfo -import com.intellij.refactoring.changeSignature.ParameterInfoImpl +import com.intellij.psi.search.searches.OverridingMethodsSearch +import com.intellij.refactoring.changeSignature.* +import com.intellij.refactoring.util.CanonicalTypes import com.intellij.usageView.UsageInfo import com.intellij.util.VisibilityUtil +import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod import org.jetbrains.kotlin.asJava.toLightMethods +import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.PropertyCodegen import org.jetbrains.kotlin.descriptors.CallableDescriptor @@ -37,16 +38,18 @@ import org.jetbrains.kotlin.idea.core.refactoring.j2k import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetMethodDescriptor.Kind import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.JetCallableDefinitionUsage +import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinCallerUsage import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.parameterIndex import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList -import java.util.* +import java.util.ArrayList +import java.util.HashMap +import java.util.LinkedHashSet import kotlin.properties.Delegates public class JetChangeInfo( @@ -57,7 +60,8 @@ public class JetChangeInfo( var newVisibility: Visibility = methodDescriptor.getVisibility(), parameterInfos: List = methodDescriptor.getParameters(), receiver: JetParameterInfo? = methodDescriptor.receiver, - val context: PsiElement + val context: PsiElement, + primaryPropagationTargets: Collection = emptyList() ): ChangeInfo { var receiverParameterInfo: JetParameterInfo? = receiver set(value: JetParameterInfo?) { @@ -148,6 +152,42 @@ public class JetChangeInfo( override fun getLanguage(): Language = JetLanguage.INSTANCE + public var propagationTargetUsageInfos: List = ArrayList() + private set + + public var primaryPropagationTargets: Collection = emptyList() + set(value: Collection) { + $primaryPropagationTargets = value + + val result = LinkedHashSet() + + fun add(element: PsiElement) { + element.unwrapped?.let { + val usageInfo = when (it) { + is JetNamedFunction, is JetConstructor<*>, is JetClassOrObject -> + KotlinCallerUsage(it as JetNamedDeclaration) + is PsiMethod -> + CallerUsageInfo(it, true, true) + else -> + return + } + + result.add(usageInfo) + } + } + + for (caller in value) { + add(caller) + OverridingMethodsSearch.search(caller.getRepresentativeLightMethod() ?: continue).forEach { add(it) } + } + + propagationTargetUsageInfos = result.toList() + } + + init { + this.primaryPropagationTargets = primaryPropagationTargets + } + public fun getNewSignature(inheritedCallable: JetCallableDefinitionUsage): String { val buffer = StringBuilder() @@ -239,13 +279,22 @@ public class JetChangeInfo( VisibilityUtil.getVisibilityModifier(currentPsiMethod.getModifierList()) else PsiModifier.PACKAGE_LOCAL - val javaChangeInfo = ChangeSignatureProcessor(getMethod().getProject(), - originalPsiMethod, - false, - newVisibility, - newName, - newReturnType ?: PsiType.VOID, - newParameters).getChangeInfo() + val propagationTargets = primaryPropagationTargets.asSequence() + .map { it.getRepresentativeLightMethod() } + .filterNotNull() + .toSet() + val javaChangeInfo = ChangeSignatureProcessor( + getMethod().getProject(), + originalPsiMethod, + false, + newVisibility, + newName, + CanonicalTypes.createTypeWrapper(newReturnType ?: PsiType.VOID), + newParameters, + arrayOf(), + propagationTargets, + emptySet() + ).getChangeInfo() javaChangeInfo.updateMethod(currentPsiMethod) return javaChangeInfo @@ -336,7 +385,7 @@ public val JetChangeInfo.kind: Kind get() = methodDescriptor.kind public val JetChangeInfo.oldName: String? get() = (methodDescriptor.getMethod() as? JetFunction)?.getName() -public val JetChangeInfo.affectedFunctions: Collection get() = methodDescriptor.affectedCallables +public fun JetChangeInfo.getAffectedCallables(): Collection = methodDescriptor.affectedCallables + propagationTargetUsageInfos public fun ChangeInfo.toJetChangeInfo(originalChangeSignatureDescriptor: JetMethodDescriptor): JetChangeInfo { val method = getMethod() as PsiMethod @@ -369,7 +418,7 @@ public fun ChangeInfo.toJetChangeInfo(originalChangeSignatureDescriptor: JetMeth name = info.getName(), type = if (oldIndex >= 0) originalParameterDescriptors[oldIndex].getType() else currentType, defaultValueForCall = defaultValueExpr)) { - currentTypeText = IdeDescriptorRenderers.SOURCE_CODE.renderType(currentType) + currentTypeText = IdeDescriptorRenderers.SOURCE_CODE_FOR_TYPE_ARGUMENTS.renderType(currentType) this } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureProcessor.kt index 78b4af28e32..7c7e00dcda9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureProcessor.kt @@ -26,6 +26,7 @@ import com.intellij.refactoring.changeSignature.ChangeSignatureProcessorBase import com.intellij.refactoring.changeSignature.ChangeSignatureUsageProcessor import com.intellij.refactoring.changeSignature.JavaChangeSignatureUsageProcessor import com.intellij.refactoring.rename.RenameUtil +import com.intellij.refactoring.rename.UnresolvableCollisionUsageInfo import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageViewDescriptor import com.intellij.util.containers.MultiMap @@ -52,7 +53,7 @@ public class JetChangeSignatureProcessor(project: Project, KotlinWrapperForJavaUsageInfos(it, javaProcessor.findUsages(it), getChangeInfo().getMethod()) } } - super.findUsages().filterIsInstanceTo(allUsages, javaClass>()) + super.findUsages().filterTo(allUsages) { it is JetUsageInfo<*> || it is UnresolvableCollisionUsageInfo } return allUsages.toTypedArray() } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java index bace90962dd..fd3bd9db57b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java @@ -28,6 +28,7 @@ import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.changeSignature.*; import com.intellij.refactoring.rename.ResolveSnapshotProvider; +import com.intellij.refactoring.rename.UnresolvableCollisionUsageInfo; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.MoveRenameUsageInfo; import com.intellij.refactoring.util.RefactoringUIUtil; @@ -54,6 +55,7 @@ import org.jetbrains.kotlin.idea.core.refactoring.RefactoringPackage; import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.*; import org.jetbrains.kotlin.idea.references.JetSimpleNameReference; import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchPackage; +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers; import org.jetbrains.kotlin.kdoc.psi.impl.KDocName; import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor; import org.jetbrains.kotlin.name.Name; @@ -68,6 +70,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; +import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind; import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode; import org.jetbrains.kotlin.resolve.scopes.JetScope; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; @@ -80,6 +83,8 @@ import java.util.*; public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsageProcessor { @Override public UsageInfo[] findUsages(ChangeInfo info) { + originalJavaMethodDescriptor = null; + Set result = new HashSet(); if (info instanceof JetChangeInfo) { @@ -89,19 +94,29 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro findSAMUsages(info, result); findConstructorDelegationUsages(info, result); findKotlinOverrides(info, result); + if (info instanceof JavaChangeInfo) { + findKotlinCallers((JavaChangeInfo) info, result); + } } return result.toArray(new UsageInfo[result.size()]); } private static void findAllMethodUsages(JetChangeInfo changeInfo, Set result) { - for (UsageInfo functionUsageInfo : ChangeSignaturePackage.getAffectedFunctions(changeInfo)) { + for (UsageInfo functionUsageInfo : ChangeSignaturePackage.getAffectedCallables(changeInfo)) { if (functionUsageInfo instanceof JetCallableDefinitionUsage) { findOneMethodUsages((JetCallableDefinitionUsage) functionUsageInfo, changeInfo, result); } + else if (functionUsageInfo instanceof KotlinCallerUsage) { + findCallerUsages((KotlinCallerUsage) functionUsageInfo, changeInfo, result); + } else { result.add(functionUsageInfo); + boolean propagationTarget = functionUsageInfo instanceof CallerUsageInfo + || (functionUsageInfo instanceof OverriderUsageInfo + && !((OverriderUsageInfo) functionUsageInfo).isOriginalOverrider()); + PsiElement callee = functionUsageInfo.getElement(); if (callee == null) continue; @@ -115,13 +130,89 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro JetCallElement callElement = PsiTreeUtil.getParentOfType(element, JetCallElement.class); JetExpression calleeExpression = callElement != null ? callElement.getCalleeExpression() : null; if (calleeExpression != null && PsiTreeUtil.isAncestor(calleeExpression, element, false)) { - result.add(new JetFunctionCallUsage(callElement, changeInfo.getMethodDescriptor().getOriginalPrimaryCallable())); + result.add(propagationTarget + ? new KotlinCallerCallUsage(callElement) + : new JetFunctionCallUsage(callElement, changeInfo.getMethodDescriptor().getOriginalPrimaryCallable())); } } } } } + private static void findCallerUsages(KotlinCallerUsage callerUsage, JetChangeInfo changeInfo, final Set result) { + result.add(callerUsage); + + JetNamedDeclaration element = callerUsage.getElement(); + if (element == null) return; + + for (PsiReference ref : ReferencesSearch.search(element, element.getUseScope())) { + PsiElement refElement = ref.getElement(); + JetCallElement callElement = PsiTreeUtil.getParentOfType(refElement, JetCallElement.class); + if (callElement != null && PsiTreeUtil.isAncestor(callElement.getCalleeExpression(), refElement, false)) { + result.add(new KotlinCallerCallUsage(callElement)); + } + } + + JetElement body = ChangeSignaturePackage.getDeclarationBody(element); + final Set newParameterNames = KotlinPackage.mapTo( + changeInfo.getNonReceiverParameters(), + new HashSet(), + new Function1() { + @Override + public String invoke(JetParameterInfo info) { + return info.getName(); + } + } + ); + if (body != null) { + final DeclarationDescriptor callerDescriptor = ResolvePackage.resolveToDescriptor(element); + final BindingContext context = ResolvePackage.analyze(body); + body.accept( + new JetTreeVisitorVoid() { + @Override + public void visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression) { + final String currentName = expression.getReferencedName(); + if (!newParameterNames.contains(currentName)) return; + + ResolvedCall resolvedCall = CallUtilPackage.getResolvedCall(expression, context); + if (resolvedCall == null) return; + + if (resolvedCall.getExplicitReceiverKind() != ExplicitReceiverKind.NO_EXPLICIT_RECEIVER) return; + + CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor(); + if (!(resultingDescriptor instanceof VariableDescriptor)) return; + + // Do not report usages of duplicated parameter + if (resultingDescriptor instanceof ValueParameterDescriptor + && resultingDescriptor.getContainingDeclaration() == callerDescriptor) return; + + JetElement callElement = resolvedCall.getCall().getCallElement(); + + ReceiverValue receiver = resolvedCall.getExtensionReceiver(); + if (!(receiver instanceof ThisReceiver)) { + receiver = resolvedCall.getDispatchReceiver(); + } + if (receiver instanceof ThisReceiver) { + result.add(new JetImplicitThisUsage(callElement, ((ThisReceiver) receiver).getDeclarationDescriptor())); + } + else if (!receiver.exists()) { + result.add( + new UnresolvableCollisionUsageInfo(callElement, null) { + @Override + public String getDescription() { + return "There is already a variable '" + currentName + "' in " + + IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(callerDescriptor) + + ". It will conflict with the new parameter."; + } + } + ); + } + } + } + ); + } + } + private static void findOneMethodUsages( @NotNull JetCallableDefinitionUsage functionUsageInfo, final JetChangeInfo changeInfo, @@ -269,7 +360,7 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro result.add(new JetImplicitThisToParameterUsage(callElement, originalReceiverInfo, functionUsageInfo)); } else { - result.add(new JetImplicitOuterThisToQualifiedThisUsage(callElement, targetDescriptor)); + result.add(new JetImplicitThisUsage(callElement, targetDescriptor)); } } @@ -375,6 +466,25 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro } } + private static void findKotlinCallers(JavaChangeInfo changeInfo, Set result) { + PsiElement method = changeInfo.getMethod(); + if (!RefactoringPackage.isTrueJavaMethod(method)) return; + + for (PsiMethod primaryCaller : changeInfo.getMethodsToPropagateParameters()) { + addDeferredCallerIfPossible(result, primaryCaller); + for (PsiMethod overridingCaller : OverridingMethodsSearch.search(primaryCaller)) { + addDeferredCallerIfPossible(result, overridingCaller); + } + } + } + + private static void addDeferredCallerIfPossible(Set result, PsiMethod overridingCaller) { + PsiElement unwrappedElement = AsJavaPackage.getNamedUnwrappedElement(overridingCaller); + if (unwrappedElement instanceof JetFunction || unwrappedElement instanceof JetClass) { + result.add(new DeferredJavaMethodKotlinCallerUsage((JetNamedDeclaration) unwrappedElement)); + } + } + private static void findDeferredUsagesOfParameters( ChangeInfo changeInfo, Set result, @@ -408,7 +518,7 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro javaMethodChangeInfo) { @NotNull @Override - protected JetUsageInfo getDelegateUsage() { + public JetUsageInfo getDelegateUsage() { return new JetParameterUsage((JetElement) element, getJavaMethodChangeInfo().getNewParameters()[paramIndex], functionInfoForParameters); @@ -428,16 +538,15 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro public MultiMap findConflicts(ChangeInfo info, Ref refUsages) { MultiMap result = new MultiMap(); - // Delete OverriderUsageInfo for Kotlin declarations since they can't be processed correctly - // TODO: Drop when OverriderUsageInfo.getElement() gets deleted + // Delete OverriderUsageInfo and CallerUsageInfo for Kotlin declarations since they can't be processed correctly + // TODO (OverriderUsageInfo only): Drop when OverriderUsageInfo.getElement() gets deleted UsageInfo[] usageInfos = refUsages.get(); List adjustedUsages = KotlinPackage.filterNot( usageInfos, new Function1() { @Override public Boolean invoke(UsageInfo info) { - return info instanceof OverriderUsageInfo && - ((OverriderUsageInfo) info).getOverridingMethod() instanceof KotlinLightMethod; + return getOverriderOrCaller(info) instanceof KotlinLightMethod; } } ); @@ -519,9 +628,47 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro findThisLabelConflicts((JetChangeInfo) info, refUsages, result, changeInfo, function); } + for (UsageInfo usageInfo : usageInfos) { + if (!(usageInfo instanceof KotlinCallerUsage)) continue; + + JetNamedDeclaration caller = (JetNamedDeclaration) usageInfo.getElement(); + DeclarationDescriptor callerDescriptor = ResolvePackage.resolveToDescriptor(caller); + + findParameterDuplicationInCaller(result, changeInfo, caller, callerDescriptor); + } + return result; } + private static void findParameterDuplicationInCaller( + MultiMap result, + JetChangeInfo changeInfo, + JetNamedDeclaration caller, + DeclarationDescriptor callerDescriptor + ) { + List valueParameters = PsiUtilPackage.getValueParameters(caller); + Map existingParameters = KotlinPackage.toMap( + valueParameters, + new Function1() { + @Override + public String invoke(JetParameter parameter) { + return parameter.getName(); + } + } + ); + for (JetParameterInfo parameterInfo : changeInfo.getNonReceiverParameters()) { + if (!(parameterInfo.getIsNewParameter())) continue; + + String name = parameterInfo.getName(); + JetParameter parameter = existingParameters.get(name); + if (parameter != null) { + result.putValue(parameter, "There is already a parameter '" + name + "' in " + + IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(callerDescriptor) + + ". It will conflict with the new parameter."); + } + } + } + private static void findThisLabelConflicts( JetChangeInfo info, Ref refUsages, @@ -666,21 +813,24 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro private JetMethodDescriptor originalJavaMethodDescriptor; private static boolean isJavaMethodUsage(UsageInfo usageInfo) { - if (usageInfo instanceof JavaMethodDeferredKotlinUsage) return true; - // MoveRenameUsageInfo corresponds to non-Java usage of Java method - return usageInfo instanceof MoveRenameUsageInfo - && RefactoringPackage.isTrueJavaMethod(((MoveRenameUsageInfo) usageInfo).getReferencedElement()); + return usageInfo instanceof JavaMethodDeferredKotlinUsage || usageInfo instanceof MoveRenameUsageInfo; } @Nullable - private static UsageInfo createReplacementUsage(UsageInfo originalUsageInfo, JetChangeInfo javaMethodChangeInfo) { + private static UsageInfo createReplacementUsage(UsageInfo originalUsageInfo, JetChangeInfo javaMethodChangeInfo, UsageInfo[] allUsages) { if (originalUsageInfo instanceof JavaMethodDeferredKotlinUsage) { return ((JavaMethodDeferredKotlinUsage) originalUsageInfo).resolve(javaMethodChangeInfo); } JetCallElement callElement = PsiTreeUtil.getParentOfType(originalUsageInfo.getElement(), JetCallElement.class); - return callElement != null ? new JavaMethodKotlinCallUsage(callElement, javaMethodChangeInfo) : null; + if (callElement == null) return null; + + PsiReference ref = originalUsageInfo.getReference(); + PsiElement refTarget = ref != null ? ref.resolve() : null; + return new JavaMethodKotlinCallUsage(callElement, + javaMethodChangeInfo, + refTarget != null && ChangeSignaturePackage.isCaller(refTarget, allUsages)); } private static class NullabilityPropagator { @@ -752,6 +902,20 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro } } + private static boolean isOverriderOrCaller(UsageInfo usage) { + return usage instanceof OverriderUsageInfo || usage instanceof CallerUsageInfo; + } + + @Nullable + private static PsiMethod getOverriderOrCaller(UsageInfo usage) { + if (usage instanceof OverriderUsageInfo) return ((OverriderUsageInfo) usage).getOverridingMethod(); + if (usage instanceof CallerUsageInfo) { + PsiElement element = usage.getElement(); + return element instanceof PsiMethod ? (PsiMethod) element : null; + } + return null; + } + @Override public boolean processUsage(ChangeInfo changeInfo, UsageInfo usageInfo, boolean beforeMethodChange, UsageInfo[] usages) { PsiElement method = changeInfo.getMethod(); @@ -772,15 +936,15 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro NullabilityPropagator nullabilityPropagator = new NullabilityPropagator(javaChangeInfo.getMethod()); for (UsageInfo usage : javaUsageInfos) { - if (usage instanceof OverriderUsageInfo && beforeMethodChange) continue; + if (isOverriderOrCaller(usage) && beforeMethodChange) continue; for (ChangeSignatureUsageProcessor processor : processors) { if (processor instanceof JetChangeSignatureUsageProcessor) continue; - if (usage instanceof OverriderUsageInfo) { + if (isOverriderOrCaller(usage)) { processor.processUsage(javaChangeInfo, usage, true, javaUsageInfos); } if (processor.processUsage(javaChangeInfo, usage, beforeMethodChange, javaUsageInfos)) break; } - if (usage instanceof OverriderUsageInfo) { + if (usage instanceof OverriderUsageInfo && ((OverriderUsageInfo) usage).isOriginalOverrider()) { PsiMethod overridingMethod = ((OverriderUsageInfo) usage).getOverridingMethod(); if (overridingMethod != null && !(overridingMethod instanceof KotlinLightMethod)) { nullabilityPropagator.processMethod(overridingMethod); @@ -791,23 +955,29 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro } if (beforeMethodChange) { - if (isJavaMethodUsage) { + boolean startedFromJava = method instanceof PsiMethod; + if (startedFromJava && originalJavaMethodDescriptor == null) { FunctionDescriptor methodDescriptor = ResolvePackage.getJavaMethodDescriptor((PsiMethod) method); - JetChangeSignatureData changeSignatureData = - new JetChangeSignatureData(methodDescriptor, method, Collections.singletonList(methodDescriptor)); - if (changeSignatureData != originalJavaMethodDescriptor) { - originalJavaMethodDescriptor = changeSignatureData; - } + originalJavaMethodDescriptor = + new JetChangeSignatureData(methodDescriptor, method, Collections.singletonList(methodDescriptor));; // This change info is used as a placeholder before primary method update // It gets replaced with real change info afterwards JetChangeInfo dummyChangeInfo = - new JetChangeInfo(originalJavaMethodDescriptor, "", null, "", Visibilities.INTERNAL, Collections.emptyList(), null, changeInfo.getMethod()); + new JetChangeInfo(originalJavaMethodDescriptor, + "", + null, + "", + Visibilities.INTERNAL, + Collections.emptyList(), + null, + changeInfo.getMethod(), + Collections.emptyList()); for (int i = 0; i < usages.length; i++) { UsageInfo oldUsageInfo = usages[i]; if (!isJavaMethodUsage(oldUsageInfo)) continue; - UsageInfo newUsageInfo = createReplacementUsage(oldUsageInfo, dummyChangeInfo); + UsageInfo newUsageInfo = createReplacementUsage(oldUsageInfo, dummyChangeInfo, usages); if (newUsageInfo != null) { usages[i] = newUsageInfo; } @@ -832,7 +1002,7 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro } if (usageInfo instanceof JavaMethodKotlinUsageWithDelegate) { - return ((JavaMethodKotlinUsageWithDelegate) usageInfo).processUsage(); + return ((JavaMethodKotlinUsageWithDelegate) usageInfo).processUsage(usages); } if (usageInfo instanceof MoveRenameUsageInfo && isJavaMethodUsage) { @@ -846,7 +1016,7 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro return false; } - return usageInfo instanceof JetUsageInfo ? ((JetUsageInfo) usageInfo).processUsage((JetChangeInfo) changeInfo, element) : true; + return usageInfo instanceof JetUsageInfo ? ((JetUsageInfo) usageInfo).processUsage((JetChangeInfo) changeInfo, element, usages) : true; } @Override @@ -855,7 +1025,7 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro JetChangeInfo jetChangeInfo = (JetChangeInfo) changeInfo; for (JetCallableDefinitionUsage primaryFunction : jetChangeInfo.getMethodDescriptor().getPrimaryCallables()) { - primaryFunction.processUsage(jetChangeInfo, primaryFunction.getDeclaration()); + primaryFunction.processUsage(jetChangeInfo, primaryFunction.getDeclaration(), UsageInfo.EMPTY_ARRAY); } jetChangeInfo.primaryMethodUpdated(); return true; diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/changeSignatureUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/changeSignatureUtils.kt new file mode 100644 index 00000000000..3effd19684b --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/changeSignatureUtils.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.changeSignature + +import com.intellij.psi.PsiElement +import com.intellij.refactoring.changeSignature.CallerUsageInfo +import com.intellij.refactoring.changeSignature.OverriderUsageInfo +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.DeferredJavaMethodKotlinCallerUsage +import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.JavaMethodKotlinUsageWithDelegate +import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinCallerUsage +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf + +private fun JetNamedDeclaration.getDeclarationBody(): JetElement? { + return when { + this is JetClassOrObject -> getDelegationSpecifierList() + this is JetPrimaryConstructor -> getContainingClassOrObject().getDelegationSpecifierList() + this is JetSecondaryConstructor -> getDelegationCall() + this is JetNamedFunction -> getBodyExpression() + else -> null + } +} + +public fun PsiElement.isCaller(allUsages: Array): Boolean { + val elementToSearch = (this as? JetClass)?.getPrimaryConstructor() ?: this + return allUsages + .asSequence() + .filter { + val usage = (it as? JavaMethodKotlinUsageWithDelegate<*>)?.delegateUsage ?: it + usage is KotlinCallerUsage + || usage is DeferredJavaMethodKotlinCallerUsage + || usage is CallerUsageInfo + || (usage is OverriderUsageInfo && !usage.isOriginalOverrider()) + } + .any { it.getElement() == elementToSearch } +} + +public fun JetElement.isInsideOfCallerBody(allUsages: Array): Boolean { + val container = parentsWithSelf.firstOrNull { + it is JetNamedFunction || it is JetConstructor<*> || it is JetClassOrObject + } as? JetNamedDeclaration ?: return false + val body = container.getDeclarationBody() ?: return false + return body.getTextRange().contains(getTextRange()) && container.isCaller(allUsages) +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/JetChangeSignatureDialog.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/JetChangeSignatureDialog.kt index 3c6d9d51f3b..949259ab460 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/JetChangeSignatureDialog.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/JetChangeSignatureDialog.kt @@ -20,7 +20,6 @@ import com.intellij.openapi.editor.colors.EditorColorsManager import com.intellij.openapi.editor.colors.EditorFontType import com.intellij.openapi.editor.event.DocumentAdapter import com.intellij.openapi.editor.event.DocumentEvent -import com.intellij.openapi.fileTypes.LanguageFileType import com.intellij.openapi.options.ConfigurationException import com.intellij.openapi.project.Project import com.intellij.openapi.ui.VerticalFlowLayout @@ -30,18 +29,15 @@ import com.intellij.psi.PsiCodeFragment import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElement import com.intellij.refactoring.BaseRefactoringProcessor -import com.intellij.refactoring.changeSignature.CallerChooserBase import com.intellij.refactoring.changeSignature.ChangeSignatureDialogBase import com.intellij.refactoring.changeSignature.MethodDescriptor import com.intellij.refactoring.changeSignature.ParameterTableModelItemBase import com.intellij.refactoring.ui.ComboBoxVisibilityPanel -import com.intellij.refactoring.ui.VisibilityPanelBase import com.intellij.ui.DottedBorder import com.intellij.ui.EditorTextField import com.intellij.ui.components.JBLabel import com.intellij.ui.treeStructure.Tree import com.intellij.util.Consumer -import com.intellij.util.Function import com.intellij.util.IJSwingUtilities import com.intellij.util.ui.UIUtil import com.intellij.util.ui.table.JBTableRow @@ -158,6 +154,10 @@ public class JetChangeSignatureDialog( override fun createCallerChooser(title: String, treeToReuse: Tree?, callback: Consumer>) = KotlinCallerChooser(myMethod.getMethod(), myProject, title, treeToReuse, callback) + // Forbid receiver propagation + override fun mayPropagateParameters() = + getParameters().any { it.isNewParameter && it != parametersTableModel.getReceiver() } + override fun getTableEditor(table: JTable, item: ParameterTableModelItemBase): JBTableRowEditor? { return object : JBTableRowEditor() { private val components = ArrayList() @@ -364,6 +364,7 @@ public class JetChangeSignatureDialog( getVisibility(), getMethodName(), myDefaultValueContext) + changeInfo.primaryPropagationTargets = myMethodsToPropagateParameters ?: emptyList(); return JetChangeSignatureProcessor(myProject, changeInfo, commandName ?: getTitle()) } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JavaMethodDeferredKotlinUsage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JavaMethodDeferredKotlinUsage.kt index 488c88f3947..8123e2d3552 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JavaMethodDeferredKotlinUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JavaMethodDeferredKotlinUsage.kt @@ -22,35 +22,41 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo import org.jetbrains.kotlin.psi.JetConstructorDelegationCall import org.jetbrains.kotlin.psi.JetFunction +import org.jetbrains.kotlin.psi.JetNamedDeclaration import org.jetbrains.kotlin.types.JetType -public abstract class JavaMethodDeferredKotlinUsage(element: T): UsageInfo(element) { - abstract fun resolve(javaMethodChangeInfo: JetChangeInfo): JavaMethodKotlinUsageWithDelegate +public abstract class JavaMethodDeferredKotlinUsage(element: T) : UsageInfo(element) { + abstract fun resolve(javaMethodChangeInfo: JetChangeInfo): JavaMethodKotlinUsageWithDelegate } public class DeferredJavaMethodOverrideOrSAMUsage( val function: JetFunction, val functionDescriptor: FunctionDescriptor, val samCallType: JetType? -): JavaMethodDeferredKotlinUsage(function) { - override fun resolve(javaMethodChangeInfo: JetChangeInfo): JavaMethodKotlinUsageWithDelegate { - return object : JavaMethodKotlinUsageWithDelegate(function, javaMethodChangeInfo) { - override val delegateUsage = JetCallableDefinitionUsage( - function, - functionDescriptor, - javaMethodChangeInfo.methodDescriptor.originalPrimaryCallable, - samCallType - ) - } +) : JavaMethodDeferredKotlinUsage(function) { + override fun resolve(javaMethodChangeInfo: JetChangeInfo): JavaMethodKotlinUsageWithDelegate { + return object : JavaMethodKotlinUsageWithDelegate(function, javaMethodChangeInfo) { + override val delegateUsage = JetCallableDefinitionUsage(function, functionDescriptor, javaMethodChangeInfo.methodDescriptor.originalPrimaryCallable, samCallType) } + } +} + +public class DeferredJavaMethodKotlinCallerUsage( + val declaration: JetNamedDeclaration +) : JavaMethodDeferredKotlinUsage(declaration) { + override fun resolve(javaMethodChangeInfo: JetChangeInfo): JavaMethodKotlinUsageWithDelegate { + return object : JavaMethodKotlinUsageWithDelegate(declaration, javaMethodChangeInfo) { + override val delegateUsage = KotlinCallerUsage(declaration) + } + } } public class JavaConstructorDeferredUsageInDelegationCall( val delegationCall: JetConstructorDelegationCall -): JavaMethodDeferredKotlinUsage(delegationCall) { - override fun resolve(javaMethodChangeInfo: JetChangeInfo): JavaMethodKotlinUsageWithDelegate { - return object : JavaMethodKotlinUsageWithDelegate(delegationCall, javaMethodChangeInfo) { - override val delegateUsage = JetConstructorDelegationCallUsage(delegationCall, javaMethodChangeInfo) - } +) : JavaMethodDeferredKotlinUsage(delegationCall) { + override fun resolve(javaMethodChangeInfo: JetChangeInfo): JavaMethodKotlinUsageWithDelegate { + return object : JavaMethodKotlinUsageWithDelegate(delegationCall, javaMethodChangeInfo) { + override val delegateUsage = JetConstructorDelegationCallUsage(delegationCall, javaMethodChangeInfo) } + } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JavaMethodKotlinUsageWithDelegate.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JavaMethodKotlinUsageWithDelegate.kt index 2a319434b91..5be6bb3f743 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JavaMethodKotlinUsageWithDelegate.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JavaMethodKotlinUsageWithDelegate.kt @@ -26,13 +26,18 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor public abstract class JavaMethodKotlinUsageWithDelegate( val psiElement: T, var javaMethodChangeInfo: JetChangeInfo): UsageInfo(psiElement) { - protected abstract val delegateUsage: JetUsageInfo + abstract val delegateUsage: JetUsageInfo - fun processUsage(): Boolean = delegateUsage.processUsage(javaMethodChangeInfo, psiElement) + fun processUsage(allUsages: Array): Boolean = delegateUsage.processUsage(javaMethodChangeInfo, psiElement, allUsages) } public class JavaMethodKotlinCallUsage( callElement: JetCallElement, - javaMethodChangeInfo: JetChangeInfo): JavaMethodKotlinUsageWithDelegate(callElement, javaMethodChangeInfo) { - override protected val delegateUsage = JetFunctionCallUsage(psiElement, javaMethodChangeInfo.methodDescriptor.originalPrimaryCallable) + javaMethodChangeInfo: JetChangeInfo, + propagationCall: Boolean): JavaMethodKotlinUsageWithDelegate(callElement, javaMethodChangeInfo) { + override val delegateUsage = if (propagationCall) { + KotlinCallerCallUsage(psiElement) + } else { + JetFunctionCallUsage(psiElement, javaMethodChangeInfo.methodDescriptor.originalPrimaryCallable) + } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetCallableDefinitionUsage.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetCallableDefinitionUsage.java index 52d2d34ae21..bd89a3ed30c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetCallableDefinitionUsage.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetCallableDefinitionUsage.java @@ -21,6 +21,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.usageView.UsageInfo; import kotlin.KotlinPackage; import kotlin.Pair; import org.jetbrains.annotations.NotNull; @@ -165,7 +166,7 @@ public class JetCallableDefinitionUsage extends JetUsageIn } @Override - public boolean processUsage(@NotNull JetChangeInfo changeInfo, @NotNull PsiElement element) { + public boolean processUsage(@NotNull JetChangeInfo changeInfo, @NotNull PsiElement element, @NotNull UsageInfo[] allUsages) { if (!(element instanceof JetNamedDeclaration)) return true; JetPsiFactory psiFactory = JetPsiFactory(element.getProject()); diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetConstructorDelegationCallUsage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetConstructorDelegationCallUsage.kt index 617b5e14139..e193950b0af 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetConstructorDelegationCallUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetConstructorDelegationCallUsage.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.refactoring.changeSignature.usages +import com.intellij.usageView.UsageInfo import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo import org.jetbrains.kotlin.psi.JetConstructorDelegationCall import org.jetbrains.kotlin.psi.JetPsiFactory @@ -27,7 +28,7 @@ public class JetConstructorDelegationCallUsage( ) : JetUsageInfo(call) { val delegate = JetFunctionCallUsage(call, changeInfo.methodDescriptor.originalPrimaryCallable) - override fun processUsage(changeInfo: JetChangeInfo, element: JetConstructorDelegationCall): Boolean { + override fun processUsage(changeInfo: JetChangeInfo, element: JetConstructorDelegationCall, allUsages: Array): Boolean { val isThisCall = element.isCallToThis() var elementToWorkWith = element @@ -36,7 +37,7 @@ public class JetConstructorDelegationCallUsage( elementToWorkWith = constructor.replaceImplicitDelegationCallWithExplicit(isThisCall) } - val result = delegate.processUsage(changeInfo, elementToWorkWith) + val result = delegate.processUsage(changeInfo, elementToWorkWith, allUsages) if (changeInfo.getNewParametersCount() == 0 && !isThisCall && !elementToWorkWith.isImplicit()) { (elementToWorkWith.getParent() as? JetSecondaryConstructor)?.getColon()?.delete() diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetEnumEntryWithoutSuperCallUsage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetEnumEntryWithoutSuperCallUsage.kt index d7e3f3cbd8a..d770acd3b2b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetEnumEntryWithoutSuperCallUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetEnumEntryWithoutSuperCallUsage.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.refactoring.changeSignature.usages +import com.intellij.usageView.UsageInfo import org.jetbrains.kotlin.psi.JetEnumEntry import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo import org.jetbrains.kotlin.psi.JetPsiFactory @@ -24,7 +25,7 @@ import org.jetbrains.kotlin.psi.JetDelegatorToSuperCall import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType public class JetEnumEntryWithoutSuperCallUsage(enumEntry: JetEnumEntry) : JetUsageInfo(enumEntry) { - override fun processUsage(changeInfo: JetChangeInfo, element: JetEnumEntry): Boolean { + override fun processUsage(changeInfo: JetChangeInfo, element: JetEnumEntry, allUsages: Array): Boolean { if (changeInfo.getNewParameters().size() > 0) { val psiFactory = JetPsiFactory(element) @@ -36,7 +37,7 @@ public class JetEnumEntryWithoutSuperCallUsage(enumEntry: JetEnumEntry) : JetUsa element.addBefore(psiFactory.createColon(), delegatorToSuperCall) return JetFunctionCallUsage(delegatorToSuperCall, changeInfo.methodDescriptor.originalPrimaryCallable) - .processUsage(changeInfo, delegatorToSuperCall) + .processUsage(changeInfo, delegatorToSuperCall, allUsages) } return true 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 e5e155b094d..7d24b78320a 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 @@ -20,6 +20,7 @@ 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; @@ -34,6 +35,7 @@ import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; import org.jetbrains.kotlin.idea.codeInsight.shorten.ShortenPackage; import org.jetbrains.kotlin.idea.core.CorePackage; +import org.jetbrains.kotlin.idea.refactoring.changeSignature.ChangeSignaturePackage; 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; @@ -89,14 +91,14 @@ public class JetFunctionCallUsage extends JetUsageInfo { } @Override - public boolean processUsage(JetChangeInfo changeInfo, JetCallElement element) { + public boolean processUsage(@NotNull JetChangeInfo changeInfo, @NotNull JetCallElement element, @NotNull UsageInfo[] allUsages) { if (shouldSkipUsage(element)) return true; changeNameIfNeeded(changeInfo, element); if (element.getValueArgumentList() != null) { if (changeInfo.isParameterSetOrOrderChanged()) { - updateArgumentsAndReceiver(changeInfo, element); + updateArgumentsAndReceiver(changeInfo, element, allUsages); } else { changeArgumentNames(changeInfo, element); @@ -319,7 +321,7 @@ public class JetFunctionCallUsage extends JetUsageInfo { return newExpression; } - private void updateArgumentsAndReceiver(JetChangeInfo changeInfo, JetCallElement element) { + private void updateArgumentsAndReceiver(JetChangeInfo changeInfo, JetCallElement element, @NotNull UsageInfo[] allUsages) { JetValueArgumentList arguments = element.getValueArgumentList(); assert arguments != null : "Argument list is expected: " + element.getText(); List oldArguments = element.getValueArguments(); @@ -347,11 +349,18 @@ public class JetFunctionCallUsage extends JetUsageInfo { } JetExpression defaultValueForCall = parameterInfo.getDefaultValueForCall(); - String defaultValueText = defaultValueForCall != null - ? substituteReferences(defaultValueForCall, - parameterInfo.getDefaultValueParameterReferences(), - psiFactory).getText() - : ""; + + String defaultValueText; + if (ChangeSignaturePackage.isInsideOfCallerBody(element, allUsages)) { + defaultValueText = parameterInfo.getName(); + } + else { + defaultValueText = defaultValueForCall != null + ? substituteReferences(defaultValueForCall, + parameterInfo.getDefaultValueParameterReferences(), + psiFactory).getText() + : ""; + } if (isNamedCall) { String newName = parameterInfo.getInheritedName(callee); diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetImplicitThisToParameterUsage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetImplicitThisToParameterUsage.kt index 72a11726f36..b85e7bcec7e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetImplicitThisToParameterUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetImplicitThisToParameterUsage.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.refactoring.changeSignature.usages +import com.intellij.usageView.UsageInfo import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetParameterInfo import org.jetbrains.kotlin.psi.JetPsiFactory @@ -32,7 +33,7 @@ public abstract class JetImplicitReceiverUsage(callElement: JetElement): JetUsag } - override fun processUsage(changeInfo: JetChangeInfo, element: JetElement): Boolean { + override fun processUsage(changeInfo: JetChangeInfo, element: JetElement, allUsages: Array): Boolean { val newQualifiedCall = JetPsiFactory(element.getProject()).createExpression( "${getNewReceiverText()}.${element.getText()}" ) as JetQualifiedExpression @@ -53,7 +54,7 @@ public class JetImplicitThisToParameterUsage( } } -public class JetImplicitOuterThisToQualifiedThisUsage( +public class JetImplicitThisUsage( callElement: JetElement, val targetDescriptor: DeclarationDescriptor ): JetImplicitReceiverUsage(callElement) { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetParameterUsage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetParameterUsage.kt index dbc6122a78d..c43224b3226 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetParameterUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetParameterUsage.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.refactoring.changeSignature.usages +import com.intellij.usageView.UsageInfo import org.jetbrains.kotlin.psi.JetSimpleNameExpression import org.jetbrains.kotlin.psi.JetPsiFactory import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo @@ -35,7 +36,7 @@ public abstract class JetExplicitReferenceUsage(element: T) : Jet } - override fun processUsage(changeInfo: JetChangeInfo, element: T): Boolean { + override fun processUsage(changeInfo: JetChangeInfo, element: T, allUsages: Array): Boolean { val newElement = JetPsiFactory(element.getProject()).createExpression(getReplacementText(changeInfo)) val elementToReplace = (element.getParent() as? JetThisExpression) ?: element processReplacedElement(elementToReplace.replace(newElement) as JetElement) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetPropertyCallUsage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetPropertyCallUsage.kt index 7ae906377bb..36f7adcf46e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetPropertyCallUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetPropertyCallUsage.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.refactoring.changeSignature.usages +import com.intellij.usageView.UsageInfo import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo import org.jetbrains.kotlin.psi.JetPsiFactory @@ -29,7 +30,7 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver public class JetPropertyCallUsage(element: JetSimpleNameExpression): JetUsageInfo(element) { private val resolvedCall = element.getResolvedCall(element.analyze()) - override fun processUsage(changeInfo: JetChangeInfo, element: JetSimpleNameExpression): Boolean { + override fun processUsage(changeInfo: JetChangeInfo, element: JetSimpleNameExpression, allUsages: Array): Boolean { updateName(changeInfo, element) updateReceiver(changeInfo, element) return true diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetUsageInfo.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetUsageInfo.kt index b07bc8bca03..9702a5a5e4f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetUsageInfo.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetUsageInfo.kt @@ -28,5 +28,5 @@ public abstract class JetUsageInfo : UsageInfo { @suppress("UNCHECKED_CAST") override fun getElement() = super.getElement() as T? - public abstract fun processUsage(changeInfo: JetChangeInfo, element: T): Boolean + public abstract fun processUsage(changeInfo: JetChangeInfo, element: T, allUsages: Array): Boolean } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinCallerUsages.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinCallerUsages.kt new file mode 100644 index 00000000000..34da97664ff --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinCallerUsages.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.changeSignature.usages + +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet +import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo +import org.jetbrains.kotlin.idea.refactoring.changeSignature.getAffectedCallables +import org.jetbrains.kotlin.idea.refactoring.changeSignature.isInsideOfCallerBody +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getValueParameterList + +public class KotlinCallerUsage(element: JetNamedDeclaration): JetUsageInfo(element) { + override fun processUsage(changeInfo: JetChangeInfo, element: JetNamedDeclaration, allUsages: Array): Boolean { + // Do not process function twice + if (changeInfo.getAffectedCallables().any { it is JetCallableDefinitionUsage<*> && it.getElement() == element }) return true + + val parameterList = when (element) { + is JetFunction -> element.getValueParameterList() + is JetClass -> element.createPrimaryConstructorParameterListIfAbsent() + else -> null + } ?: return true + val psiFactory = JetPsiFactory(getProject()) + changeInfo.getNonReceiverParameters() + .withIndex() + .filter { it.value.isNewParameter } + .forEach { + val parameterText = it.value.getDeclarationSignature(it.index, changeInfo.methodDescriptor.originalPrimaryCallable) + parameterList + .addParameter(psiFactory.createParameter(parameterText)) + .addToShorteningWaitSet() + } + + return true + } +} + +public class KotlinCallerCallUsage(element: JetCallElement): JetUsageInfo(element) { + override fun processUsage(changeInfo: JetChangeInfo, element: JetCallElement, allUsages: Array): Boolean { + val argumentList = element.getValueArgumentList() ?: return true + val psiFactory = JetPsiFactory(getProject()) + val isNamedCall = argumentList.getArguments().any { it.getArgumentName() != null } + changeInfo.getNonReceiverParameters() + .filter { it.isNewParameter } + .forEach { + val parameterName = it.getName() + val argumentExpression = if (element.isInsideOfCallerBody(allUsages)) { + psiFactory.createExpression(parameterName) + } + else { + it.defaultValueForCall ?: psiFactory.createExpression("_") + } + val argument = psiFactory.createArgument( + expression = argumentExpression, + name = if (isNamedCall) Name.identifier(parameterName) else null + ) + argumentList.addArgument(argument) + } + + return true + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceParameter/KotlinIntroduceParameterMethodUsageProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceParameter/KotlinIntroduceParameterMethodUsageProcessor.kt index 3cbb070c9f4..ae465fde734 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceParameter/KotlinIntroduceParameterMethodUsageProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/introduceParameter/KotlinIntroduceParameterMethodUsageProcessor.kt @@ -100,7 +100,7 @@ public class KotlinIntroduceParameterMethodUsageProcessor : IntroduceParameterMe .map { it.unwrapped } .filterIsInstance() return (kotlinFunctions + element).all { - JetCallableDefinitionUsage(it, changeInfo.originalBaseFunctionDescriptor, null, null).processUsage(changeInfo, it) + JetCallableDefinitionUsage(it, changeInfo.originalBaseFunctionDescriptor, null, null).processUsage(changeInfo, it, usages) } } @@ -116,7 +116,7 @@ public class KotlinIntroduceParameterMethodUsageProcessor : IntroduceParameterMe else { JetFunctionCallUsage(callElement, changeInfo.methodDescriptor.originalPrimaryCallable) } - return delegateUsage.processUsage(changeInfo, callElement) + return delegateUsage.processUsage(changeInfo, callElement, usages) } override fun processAddSuperCall(data: IntroduceParameterData, usage: UsageInfo, usages: Array): Boolean = true diff --git a/idea/testData/refactoring/changeSignature/JavaConstructorParameterPropagationAfter.1.kt b/idea/testData/refactoring/changeSignature/JavaConstructorParameterPropagationAfter.1.kt new file mode 100644 index 00000000000..281d5538d87 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/JavaConstructorParameterPropagationAfter.1.kt @@ -0,0 +1,18 @@ +open class C(n: Int) : A(n) { + +} + +open class D: A { + constructor(n: Int): super(n) + constructor(b: Boolean, n: Int) : super(n) +} + +fun test(n: Int) { + A(n) + A(false, n) + B(n) + B(false, n) + C(n) + D(n) + D(false, n) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/JavaConstructorParameterPropagationAfter.java b/idea/testData/refactoring/changeSignature/JavaConstructorParameterPropagationAfter.java new file mode 100644 index 00000000000..42e2d9eab87 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/JavaConstructorParameterPropagationAfter.java @@ -0,0 +1,32 @@ +class A { + public A(int n) { + + } + + public A(boolean b, int n) { + this(n); + } +} + +class B extends A { + public B(int n) { + + super(n); + } + + public B(boolean b, int n) { + super(n); + } +} + +class Test { + void test(int n) { + new A(n); + new A(true, n); + new B(n); + new B(true, n); + new C(n); + new D(n); + new D(true, n); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/JavaConstructorParameterPropagationBefore.1.kt b/idea/testData/refactoring/changeSignature/JavaConstructorParameterPropagationBefore.1.kt new file mode 100644 index 00000000000..e19235cd9de --- /dev/null +++ b/idea/testData/refactoring/changeSignature/JavaConstructorParameterPropagationBefore.1.kt @@ -0,0 +1,18 @@ +open class C: A() { + +} + +open class D: A { + constructor(): super() + constructor(b: Boolean) +} + +fun test() { + A() + A(false) + B() + B(false) + C() + D() + D(false) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/JavaConstructorParameterPropagationBefore.java b/idea/testData/refactoring/changeSignature/JavaConstructorParameterPropagationBefore.java new file mode 100644 index 00000000000..68d9991540a --- /dev/null +++ b/idea/testData/refactoring/changeSignature/JavaConstructorParameterPropagationBefore.java @@ -0,0 +1,31 @@ +class A { + public A() { + + } + + public A(boolean b) { + this(); + } +} + +class B extends A { + public B() { + + } + + public B(boolean b) { + super(); + } +} + +class Test { + void test() { + new A(); + new A(true); + new B(); + new B(true); + new C(); + new D(); + new D(true); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/JavaParameterPropagationAfter.1.kt b/idea/testData/refactoring/changeSignature/JavaParameterPropagationAfter.1.kt new file mode 100644 index 00000000000..d6c4345f095 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/JavaParameterPropagationAfter.1.kt @@ -0,0 +1,28 @@ +open class C: A() { + override fun foo(n: Int, s: String): Unit { + + } + + override fun bar(b: Boolean, n: Int, s: String) { + foo(n, s) + } + + override fun baz() { + foo(1, "abc") + bar(false, 1, "abc") + } +} + +fun test(n: Int, s: String) { + A().foo(n, s) + A().bar(true, n, s) + A().baz() + + B().foo(n, s) + B().bar(true, n, s) + B().baz() + + C().foo(n, s) + C().bar(true, n, s) + C().baz() +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/JavaParameterPropagationAfter.java b/idea/testData/refactoring/changeSignature/JavaParameterPropagationAfter.java new file mode 100644 index 00000000000..1fd062377c9 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/JavaParameterPropagationAfter.java @@ -0,0 +1,45 @@ +class A { + public void foo(int n, String s) { + + } + + public void bar(boolean b, int n, String s) { + foo(n, s); + } + + public void baz() { + foo(1, "abc"); + bar(false, 1, "abc"); + } +} + +class B extends A { + public void foo(int n, String s) { + + } + + public void bar(boolean b, int n, String s) { + foo(1, "abc"); + } + + public void baz() { + foo(1, "abc"); + bar(false, 1, "abc"); + } +} + +class Test { + void test() { + new A().foo(1, "abc"); + new A().bar(true, 1, "abc"); + new A().baz(); + + new B().foo(1, "abc"); + new B().bar(true, 1, "abc"); + new B().baz(); + + new C().foo(1, "abc"); + new C().bar(true, 1, "abc"); + new C().baz(); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/JavaParameterPropagationBefore.1.kt b/idea/testData/refactoring/changeSignature/JavaParameterPropagationBefore.1.kt new file mode 100644 index 00000000000..736e32f45ac --- /dev/null +++ b/idea/testData/refactoring/changeSignature/JavaParameterPropagationBefore.1.kt @@ -0,0 +1,28 @@ +open class C: A() { + override fun foo() { + + } + + override fun bar(b: Boolean) { + foo() + } + + override fun baz() { + foo() + bar(false) + } +} + +fun test() { + A().foo() + A().bar(true) + A().baz() + + B().foo() + B().bar(true) + B().baz() + + C().foo() + C().bar(true) + C().baz() +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/JavaParameterPropagationBefore.java b/idea/testData/refactoring/changeSignature/JavaParameterPropagationBefore.java new file mode 100644 index 00000000000..a9567ca9520 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/JavaParameterPropagationBefore.java @@ -0,0 +1,45 @@ +class A { + public void foo() { + + } + + public void bar(boolean b) { + foo(); + } + + public void baz() { + foo(); + bar(false); + } +} + +class B extends A { + public void foo() { + + } + + public void bar(boolean b) { + foo(); + } + + public void baz() { + foo(); + bar(false); + } +} + +class Test { + void test() { + new A().foo(); + new A().bar(true); + new A().baz(); + + new B().foo(); + new B().bar(true); + new B().baz(); + + new C().foo(); + new C().bar(true); + new C().baz(); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/ParameterPropagationAfter.1.java b/idea/testData/refactoring/changeSignature/ParameterPropagationAfter.1.java new file mode 100644 index 00000000000..3784b1d3122 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ParameterPropagationAfter.1.java @@ -0,0 +1,35 @@ +import org.jetbrains.annotations.NotNull; + +class J extends A { + @Override + public void foo(int n, @NotNull String s) { + + } + + @Override + public void bar(boolean b, int n, String s) { + foo(1, "abc"); // Propagated parameters are not passed to calles in overriding methods + } + + @Override + public void baz() { + foo(1, "abc"); + bar(false, 1, "abc"); + } +} + +class Test { + void test() { + new A().foo(1, "abc"); + new A().bar(true, 1, "abc"); + new A().baz(); + + new B().foo(1, "abc"); + new B().bar(true, 1, "abc"); + new B().baz(); + + new J().foo(1, "abc"); + new J().bar(true, 1, "abc"); + new J().baz(); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/ParameterPropagationAfter.kt b/idea/testData/refactoring/changeSignature/ParameterPropagationAfter.kt new file mode 100644 index 00000000000..07e77360641 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ParameterPropagationAfter.kt @@ -0,0 +1,43 @@ +open class A { + open fun foo(n: Int, s: String) { + + } + + open fun bar(b: Boolean, n: Int, s: String) { + foo(n, s) + } + + open fun baz() { + foo(1, "abc") + bar(false, 1, "abc") + } +} + +class B : A() { + override fun foo(n: Int, s: String) { + + } + + override fun bar(b: Boolean, n: Int, s: String) { + foo(n, s) + } + + override fun baz() { + foo(1, "abc") + bar(false, 1, "abc") + } +} + +fun test(n: Int, s: String) { + A().foo(n, s) + A().bar(true, n, s) + A().baz() + + B().foo(n, s) + B().bar(true, n, s) + B().baz() + + J().foo(n, s) + J().bar(true, n, s) + J().baz() +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/ParameterPropagationBefore.1.java b/idea/testData/refactoring/changeSignature/ParameterPropagationBefore.1.java new file mode 100644 index 00000000000..91fd31f2f9f --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ParameterPropagationBefore.1.java @@ -0,0 +1,33 @@ +class J extends A { + @Override + public void foo() { + + } + + @Override + public void bar(boolean b) { + foo(); // Propagated parameters are not passed to calles in overriding methods + } + + @Override + public void baz() { + foo(); + bar(false); + } +} + +class Test { + void test() { + new A().foo(); + new A().bar(true); + new A().baz(); + + new B().foo(); + new B().bar(true); + new B().baz(); + + new J().foo(); + new J().bar(true); + new J().baz(); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/ParameterPropagationBefore.kt b/idea/testData/refactoring/changeSignature/ParameterPropagationBefore.kt new file mode 100644 index 00000000000..d9128170eaa --- /dev/null +++ b/idea/testData/refactoring/changeSignature/ParameterPropagationBefore.kt @@ -0,0 +1,43 @@ +open class A { + open fun foo() { + + } + + open fun bar(b: Boolean) { + foo() + } + + open fun baz() { + foo() + bar(false) + } +} + +class B : A() { + override fun foo() { + + } + + override fun bar(b: Boolean) { + foo() + } + + override fun baz() { + foo() + bar(false) + } +} + +fun test() { + A().foo() + A().bar(true) + A().baz() + + B().foo() + B().bar(true) + B().baz() + + J().foo() + J().bar(true) + J().baz() +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/PrimaryConstructorParameterPropagationAfter.1.java b/idea/testData/refactoring/changeSignature/PrimaryConstructorParameterPropagationAfter.1.java new file mode 100644 index 00000000000..4a5357d8bb4 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/PrimaryConstructorParameterPropagationAfter.1.java @@ -0,0 +1,22 @@ +class J extends A { + public J(int n) { + + super(n); + } + + public J(boolean b, int n) { + super(n); + } +} + +class Test { + void test(int n) { + new A(n); + new A(true, n); + new B(n); + new B(true, n); + new C(true, n); + new J(n); + new J(true, n); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/PrimaryConstructorParameterPropagationAfter.kt b/idea/testData/refactoring/changeSignature/PrimaryConstructorParameterPropagationAfter.kt new file mode 100644 index 00000000000..0963b2aa7c7 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/PrimaryConstructorParameterPropagationAfter.kt @@ -0,0 +1,21 @@ +open class A(n: Int) { + constructor(b: Boolean, n: Int): this(n) +} + +open class B: A { + constructor(n: Int) : super(n) + + constructor(b: Boolean, n: Int): super(n) +} + +open class C(b: Boolean, n: Int): A(n) + +fun test(n: Int) { + A(n) + A(false, n) + B(n) + B(false, n) + C(false, n) + J(n) + J(false, n) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/PrimaryConstructorParameterPropagationBefore.1.java b/idea/testData/refactoring/changeSignature/PrimaryConstructorParameterPropagationBefore.1.java new file mode 100644 index 00000000000..647bfef6bee --- /dev/null +++ b/idea/testData/refactoring/changeSignature/PrimaryConstructorParameterPropagationBefore.1.java @@ -0,0 +1,21 @@ +class J extends A { + public J() { + + } + + public J(boolean b) { + super(); + } +} + +class Test { + void test() { + new A(); + new A(true); + new B(); + new B(true); + new C(true); + new J(); + new J(true); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/PrimaryConstructorParameterPropagationBefore.kt b/idea/testData/refactoring/changeSignature/PrimaryConstructorParameterPropagationBefore.kt new file mode 100644 index 00000000000..b7570bd838a --- /dev/null +++ b/idea/testData/refactoring/changeSignature/PrimaryConstructorParameterPropagationBefore.kt @@ -0,0 +1,20 @@ +open class A() { + constructor(b: Boolean): this() +} + +open class B: A { + constructor() + constructor(b: Boolean): super() +} + +open class C(b: Boolean): A() + +fun test() { + A() + A(false) + B() + B(false) + C(false) + J() + J(false) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/PropagateWithParameterDuplicationBefore.kt b/idea/testData/refactoring/changeSignature/PropagateWithParameterDuplicationBefore.kt new file mode 100644 index 00000000000..b73e852988b --- /dev/null +++ b/idea/testData/refactoring/changeSignature/PropagateWithParameterDuplicationBefore.kt @@ -0,0 +1,5 @@ +fun foo() = 1 + +fun bar(n: Int): Int { + return foo() + n +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/PropagateWithParameterDuplicationMessages.txt b/idea/testData/refactoring/changeSignature/PropagateWithParameterDuplicationMessages.txt new file mode 100644 index 00000000000..a262d240b77 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/PropagateWithParameterDuplicationMessages.txt @@ -0,0 +1 @@ +There is already a parameter 'n' in fun bar(n: Int): Int. It will conflict with the new parameter. \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/PropagateWithThisQualificationInClassMemberAfter.kt b/idea/testData/refactoring/changeSignature/PropagateWithThisQualificationInClassMemberAfter.kt new file mode 100644 index 00000000000..383e6903700 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/PropagateWithThisQualificationInClassMemberAfter.kt @@ -0,0 +1,7 @@ +fun foo(n: Int): Int = 1 + +class A(val n: Int) { + fun bar(n: Int): Int { + return foo(n) + this.n + } +} diff --git a/idea/testData/refactoring/changeSignature/PropagateWithThisQualificationInClassMemberBefore.kt b/idea/testData/refactoring/changeSignature/PropagateWithThisQualificationInClassMemberBefore.kt new file mode 100644 index 00000000000..6ed7e4cef1f --- /dev/null +++ b/idea/testData/refactoring/changeSignature/PropagateWithThisQualificationInClassMemberBefore.kt @@ -0,0 +1,7 @@ +fun foo(): Int = 1 + +class A(val n: Int) { + fun bar(): Int { + return foo() + n + } +} diff --git a/idea/testData/refactoring/changeSignature/PropagateWithThisQualificationInExtensionAfter.kt b/idea/testData/refactoring/changeSignature/PropagateWithThisQualificationInExtensionAfter.kt new file mode 100644 index 00000000000..01c6a4d0bf3 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/PropagateWithThisQualificationInExtensionAfter.kt @@ -0,0 +1,7 @@ +fun foo(n: Int): Int = 1 + +class A(val n: Int) + +fun A.bar(n: Int): Int { + return foo(n) + this.n +} diff --git a/idea/testData/refactoring/changeSignature/PropagateWithThisQualificationInExtensionBefore.kt b/idea/testData/refactoring/changeSignature/PropagateWithThisQualificationInExtensionBefore.kt new file mode 100644 index 00000000000..978ac747033 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/PropagateWithThisQualificationInExtensionBefore.kt @@ -0,0 +1,7 @@ +fun foo(): Int = 1 + +class A(val n: Int) + +fun A.bar(): Int { + return foo() + n +} diff --git a/idea/testData/refactoring/changeSignature/PropagateWithVariableDuplicationBefore.kt b/idea/testData/refactoring/changeSignature/PropagateWithVariableDuplicationBefore.kt new file mode 100644 index 00000000000..ce9ddaabb08 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/PropagateWithVariableDuplicationBefore.kt @@ -0,0 +1,7 @@ +fun foo() = 1 + +val n = 1 + +fun bar(): Int { + return foo() + n +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/PropagateWithVariableDuplicationMessages.txt b/idea/testData/refactoring/changeSignature/PropagateWithVariableDuplicationMessages.txt new file mode 100644 index 00000000000..86c4632a539 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/PropagateWithVariableDuplicationMessages.txt @@ -0,0 +1 @@ +There is already a variable 'n' in fun bar(): Int. It will conflict with the new parameter. \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/SecondaryConstructorParameterPropagationAfter.1.java b/idea/testData/refactoring/changeSignature/SecondaryConstructorParameterPropagationAfter.1.java new file mode 100644 index 00000000000..4a5357d8bb4 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/SecondaryConstructorParameterPropagationAfter.1.java @@ -0,0 +1,22 @@ +class J extends A { + public J(int n) { + + super(n); + } + + public J(boolean b, int n) { + super(n); + } +} + +class Test { + void test(int n) { + new A(n); + new A(true, n); + new B(n); + new B(true, n); + new C(true, n); + new J(n); + new J(true, n); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/SecondaryConstructorParameterPropagationAfter.kt b/idea/testData/refactoring/changeSignature/SecondaryConstructorParameterPropagationAfter.kt new file mode 100644 index 00000000000..e3445e7b100 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/SecondaryConstructorParameterPropagationAfter.kt @@ -0,0 +1,21 @@ +open class A { + constructor(n: Int) + constructor(b: Boolean, n: Int): this(n) +} + +open class B: A { + constructor(n: Int) : super(n) + + constructor(b: Boolean, n: Int): super(n) +} + +open class C(b: Boolean, n: Int): A(n) + +fun test(n: Int) { + A(n) + A(false, n) + B(n) + B(false, n) + C(false, n) + J(n) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/SecondaryConstructorParameterPropagationBefore.1.java b/idea/testData/refactoring/changeSignature/SecondaryConstructorParameterPropagationBefore.1.java new file mode 100644 index 00000000000..647bfef6bee --- /dev/null +++ b/idea/testData/refactoring/changeSignature/SecondaryConstructorParameterPropagationBefore.1.java @@ -0,0 +1,21 @@ +class J extends A { + public J() { + + } + + public J(boolean b) { + super(); + } +} + +class Test { + void test() { + new A(); + new A(true); + new B(); + new B(true); + new C(true); + new J(); + new J(true); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/SecondaryConstructorParameterPropagationBefore.kt b/idea/testData/refactoring/changeSignature/SecondaryConstructorParameterPropagationBefore.kt new file mode 100644 index 00000000000..3f7a8e485d1 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/SecondaryConstructorParameterPropagationBefore.kt @@ -0,0 +1,20 @@ +open class A { + constructor() + constructor(b: Boolean): this() +} + +open class B: A { + constructor() + constructor(b: Boolean): super() +} + +open class C(b: Boolean): A() + +fun test() { + A() + A(false) + B() + B(false) + C(false) + J() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureTest.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureTest.java index 3176fdee441..f74fb00a8c4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureTest.java @@ -22,27 +22,32 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; +import com.intellij.psi.impl.java.stubs.index.JavaFullClassNameIndex; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.refactoring.BaseRefactoringProcessor; import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor; import com.intellij.refactoring.changeSignature.ParameterInfoImpl; import com.intellij.refactoring.changeSignature.ThrownExceptionInfo; +import com.intellij.refactoring.util.CanonicalTypes; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.VisibilityUtil; +import kotlin.KotlinPackage; +import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.asJava.AsJavaPackage; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.Visibilities; import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; import org.jetbrains.kotlin.idea.refactoring.JetRefactoringBundle; +import org.jetbrains.kotlin.idea.refactoring.changeSignature.ui.KotlinMethodNode; +import org.jetbrains.kotlin.idea.stubindex.JetFullClassNameIndex; +import org.jetbrains.kotlin.idea.stubindex.JetTopLevelFunctionFqnNameIndex; import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils; import org.jetbrains.kotlin.idea.test.KotlinCodeInsightTestCase; import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; -import org.jetbrains.kotlin.psi.JetElement; -import org.jetbrains.kotlin.psi.JetFile; -import org.jetbrains.kotlin.psi.JetPsiFactory; +import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.dataClassUtils.DataClassUtilsPackage; import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode; @@ -544,7 +549,7 @@ public class JetChangeSignatureTest extends KotlinCodeInsightTestCase { public void testSAMChangeMethodReturnType() throws Exception { doJavaTest( new JavaRefactoringProvider() { - @Nullable + @NotNull @Override PsiType getNewReturnType(@NotNull PsiMethod method) { return PsiType.getJavaLangObject(getPsiManager(), GlobalSearchScope.allScope(getProject())); @@ -567,7 +572,7 @@ public class JetChangeSignatureTest extends KotlinCodeInsightTestCase { return newParameters; } - @Nullable + @NotNull @Override PsiType getNewReturnType(@NotNull PsiMethod method) { return factory.createTypeFromText("X>", method); @@ -978,7 +983,7 @@ public class JetChangeSignatureTest extends KotlinCodeInsightTestCase { public void testJavaMethodOverridesReplaceParam() throws Exception { doJavaTest( new JavaRefactoringProvider() { - @Nullable + @NotNull @Override PsiType getNewReturnType(@NotNull PsiMethod method) { return PsiType.getJavaLangString(getPsiManager(), GlobalSearchScope.allScope(getProject())); @@ -998,7 +1003,7 @@ public class JetChangeSignatureTest extends KotlinCodeInsightTestCase { public void testJavaMethodOverridesChangeParam() throws Exception { doJavaTest( new JavaRefactoringProvider() { - @Nullable + @NotNull @Override PsiType getNewReturnType(@NotNull PsiMethod method) { return PsiType.getJavaLangString(getPsiManager(), GlobalSearchScope.allScope(getProject())); @@ -1084,6 +1089,233 @@ public class JetChangeSignatureTest extends KotlinCodeInsightTestCase { doTest(changeInfo); } + public void testParameterPropagation() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + + JetParameterInfo newParameter1 = new JetParameterInfo(changeInfo.getMethodDescriptor().getBaseDescriptor(), + -1, "n", null, null, + new JetPsiFactory(getProject()).createExpression("1"), JetValVar.None, null); + newParameter1.setCurrentTypeText("kotlin.Int"); + changeInfo.addParameter(newParameter1); + + JetParameterInfo newParameter2 = new JetParameterInfo(changeInfo.getMethodDescriptor().getBaseDescriptor(), + -1, "s", null, null, + new JetPsiFactory(getProject()).createExpression("\"abc\""), JetValVar.None, null); + newParameter2.setCurrentTypeText("kotlin.String"); + changeInfo.addParameter(newParameter2); + + JetClassOrObject classA = + JetFullClassNameIndex.getInstance().get("A", getProject(), GlobalSearchScope.allScope(getProject())) + .iterator().next(); + JetDeclaration functionBar = KotlinPackage.first( + classA.getDeclarations(), + new Function1() { + @Override + public Boolean invoke(JetDeclaration declaration) { + return declaration instanceof JetNamedFunction && "bar".equals(declaration.getName()); + } + } + ); + JetNamedFunction functionTest = + JetTopLevelFunctionFqnNameIndex.getInstance().get("test", getProject(), GlobalSearchScope.allScope(getProject())) + .iterator().next(); + + changeInfo.setPrimaryPropagationTargets(Arrays.asList(functionBar, functionTest)); + + doTest(changeInfo); + } + + public void testJavaParameterPropagation() throws Exception { + doJavaTest( + new JavaRefactoringProvider() { + @NotNull + @Override + ParameterInfoImpl[] getNewParameters(@NotNull PsiMethod method) { + return new ParameterInfoImpl[] { + new ParameterInfoImpl(-1, "n", PsiType.INT, "1"), + new ParameterInfoImpl(-1, + "s", + PsiType.getJavaLangString(getPsiManager(), GlobalSearchScope.allScope(getProject())), + "\"abc\"") + }; + } + + @NotNull + @Override + Set getParameterPropagationTargets(@NotNull PsiMethod method) { + PsiClass classA = KotlinPackage.first( + JavaFullClassNameIndex.getInstance() + .get("A".hashCode(), getProject(), GlobalSearchScope.allScope(getProject())), + new Function1() { + @Override + public Boolean invoke(PsiClass aClass) { + return "A".equals(aClass.getName()); + } + } + ); + PsiMethod methodBar = KotlinPackage.first( + classA.getMethods(), + new Function1() { + @Override + public Boolean invoke(PsiMethod method) { + return "bar".equals(method.getName()); + } + } + ); + JetNamedFunction functionTest = + JetTopLevelFunctionFqnNameIndex.getInstance().get("test", getProject(), GlobalSearchScope.allScope(getProject())) + .iterator().next(); + + return KotlinPackage.setOf(methodBar, AsJavaPackage.getRepresentativeLightMethod(functionTest)); + } + } + ); + } + + public void testPropagateWithParameterDuplication() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + + JetParameterInfo newParameter = new JetParameterInfo(changeInfo.getMethodDescriptor().getBaseDescriptor(), + -1, "n", KotlinBuiltIns.getInstance().getIntType(), null, + new JetPsiFactory(getProject()).createExpression("1"), JetValVar.None, null); + changeInfo.addParameter(newParameter); + + JetNamedFunction functionBar = + JetTopLevelFunctionFqnNameIndex.getInstance().get("bar", getProject(), GlobalSearchScope.allScope(getProject())) + .iterator().next(); + + changeInfo.setPrimaryPropagationTargets(Collections.singletonList(functionBar)); + + doTestConflict(changeInfo); + } + + public void testPropagateWithVariableDuplication() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + + JetParameterInfo newParameter = new JetParameterInfo(changeInfo.getMethodDescriptor().getBaseDescriptor(), + -1, "n", KotlinBuiltIns.getInstance().getIntType(), null, + new JetPsiFactory(getProject()).createExpression("1"), JetValVar.None, null); + changeInfo.addParameter(newParameter); + + JetNamedFunction functionBar = + JetTopLevelFunctionFqnNameIndex.getInstance().get("bar", getProject(), GlobalSearchScope.allScope(getProject())) + .iterator().next(); + + changeInfo.setPrimaryPropagationTargets(Collections.singletonList(functionBar)); + + doTestConflict(changeInfo); + } + + public void testPropagateWithThisQualificationInClassMember() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + + JetParameterInfo newParameter = new JetParameterInfo(changeInfo.getMethodDescriptor().getBaseDescriptor(), + -1, "n", KotlinBuiltIns.getInstance().getIntType(), null, + new JetPsiFactory(getProject()).createExpression("1"), JetValVar.None, null); + changeInfo.addParameter(newParameter); + + JetClassOrObject classA = + JetFullClassNameIndex.getInstance().get("A", getProject(), GlobalSearchScope.allScope(getProject())) + .iterator().next(); + JetDeclaration functionBar = KotlinPackage.first( + classA.getDeclarations(), + new Function1() { + @Override + public Boolean invoke(JetDeclaration declaration) { + return declaration instanceof JetNamedFunction && "bar".equals(declaration.getName()); + } + } + ); + changeInfo.setPrimaryPropagationTargets(Collections.singletonList(functionBar)); + + doTest(changeInfo); + } + + public void testPropagateWithThisQualificationInExtension() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + + JetParameterInfo newParameter = new JetParameterInfo(changeInfo.getMethodDescriptor().getBaseDescriptor(), + -1, "n", KotlinBuiltIns.getInstance().getIntType(), null, + new JetPsiFactory(getProject()).createExpression("1"), JetValVar.None, null); + changeInfo.addParameter(newParameter); + + JetNamedFunction functionBar = + JetTopLevelFunctionFqnNameIndex.getInstance().get("bar", getProject(), GlobalSearchScope.allScope(getProject())) + .iterator().next(); + + changeInfo.setPrimaryPropagationTargets(Collections.singletonList(functionBar)); + + doTest(changeInfo); + } + + public void testJavaConstructorParameterPropagation() throws Exception { + doJavaTest( + new JavaRefactoringProvider() { + @NotNull + @Override + ParameterInfoImpl[] getNewParameters(@NotNull PsiMethod method) { + return new ParameterInfoImpl[] { new ParameterInfoImpl(-1, "n", PsiType.INT, "1") }; + } + + @NotNull + @Override + Set getParameterPropagationTargets(@NotNull PsiMethod method) { + return findCallers(method); + } + } + ); + } + + public void testPrimaryConstructorParameterPropagation() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + + JetParameterInfo newParameter = new JetParameterInfo(changeInfo.getMethodDescriptor().getBaseDescriptor(), + -1, "n", KotlinBuiltIns.getInstance().getIntType(), null, + new JetPsiFactory(getProject()).createExpression("1"), JetValVar.None, null); + changeInfo.addParameter(newParameter); + + PsiMethod constructor = AsJavaPackage.getRepresentativeLightMethod(changeInfo.getMethod()); + assert constructor != null; + changeInfo.setPrimaryPropagationTargets(findCallers(constructor)); + + doTest(changeInfo); + } + + public void testSecondaryConstructorParameterPropagation() throws Exception { + JetChangeInfo changeInfo = getChangeInfo(); + + JetParameterInfo newParameter = new JetParameterInfo(changeInfo.getMethodDescriptor().getBaseDescriptor(), + -1, "n", KotlinBuiltIns.getInstance().getIntType(), null, + new JetPsiFactory(getProject()).createExpression("1"), JetValVar.None, null); + changeInfo.addParameter(newParameter); + + PsiMethod constructor = AsJavaPackage.getRepresentativeLightMethod(changeInfo.getMethod()); + assert constructor != null; + changeInfo.setPrimaryPropagationTargets(findCallers(constructor)); + + doTest(changeInfo); + } + + @NotNull + private LinkedHashSet findCallers(@NotNull PsiMethod method) { + KotlinMethodNode rootNode = + new KotlinMethodNode(method, + new HashSet(), + getProject(), + new Runnable() { + @Override + public void run() { + + } + }); + LinkedHashSet callers = new LinkedHashSet(); + for (int i = 0; i < rootNode.getChildCount(); i++) { + PsiElement element = ((KotlinMethodNode) rootNode.getChildAt(i)).getMethod(); + callers.addAll(AsJavaPackage.toLightMethods(element)); + } + return callers; + } + @NotNull @Override protected String getTestDataPath() { @@ -1142,9 +1374,10 @@ public class JetChangeSignatureTest extends KotlinCodeInsightTestCase { return method.getName(); } - @Nullable + @NotNull PsiType getNewReturnType(@NotNull PsiMethod method) { - return method.getReturnType(); + PsiType type = method.getReturnType(); + return type != null ? type : PsiType.VOID; } @NotNull @@ -1158,6 +1391,11 @@ public class JetChangeSignatureTest extends KotlinCodeInsightTestCase { return parameterInfos; } + @NotNull + Set getParameterPropagationTargets(@NotNull PsiMethod method) { + return Collections.emptySet(); + } + @NotNull final ChangeSignatureProcessor getProcessor(@NotNull PsiMethod method) { return new ChangeSignatureProcessor( @@ -1166,9 +1404,11 @@ public class JetChangeSignatureTest extends KotlinCodeInsightTestCase { false, VisibilityUtil.getVisibilityModifier(method.getModifierList()), getNewName(method), - getNewReturnType(method), + CanonicalTypes.createTypeWrapper(getNewReturnType(method)), getNewParameters(method), - new ThrownExceptionInfo[0]); + new ThrownExceptionInfo[0], + getParameterPropagationTargets(method), + Collections.emptySet()); } }