diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt index 5dcc079b0f2..d0b5de5a87c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt @@ -47,6 +47,8 @@ import org.jetbrains.kotlin.idea.core.compareDescriptors import org.jetbrains.kotlin.idea.refactoring.* import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.* import org.jetbrains.kotlin.idea.refactoring.rename.noReceivers +import org.jetbrains.kotlin.idea.references.KtArrayAccessReference +import org.jetbrains.kotlin.idea.references.KtInvokeFunctionReference import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions @@ -227,24 +229,30 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor { val functionPsi = functionUsageInfo.element ?: return for (reference in findReferences(functionPsi)) { - val element = reference.element + val element = reference.element ?: continue - if (element is KtReferenceExpression) { - var parent = element.parent + when { + reference is KtInvokeFunctionReference || reference is KtArrayAccessReference -> { + result.add(KotlinByConventionCallUsage(element as KtExpression, functionUsageInfo)) + } - when { - parent is KtCallExpression -> - result.add(KotlinFunctionCallUsage(parent, functionUsageInfo)) + element is KtReferenceExpression -> { + var parent = element.parent - parent is KtUserType && parent.parent is KtTypeReference -> { - parent = parent.parent.parent + when { + parent is KtCallExpression -> + result.add(KotlinFunctionCallUsage(parent, functionUsageInfo)) - if (parent is KtConstructorCalleeExpression && parent.parent is KtSuperTypeCallEntry) - result.add(KotlinFunctionCallUsage(parent.parent as KtSuperTypeCallEntry, functionUsageInfo)) + parent is KtUserType && parent.parent is KtTypeReference -> { + parent = parent.parent.parent + + if (parent is KtConstructorCalleeExpression && parent.parent is KtSuperTypeCallEntry) + result.add(KotlinFunctionCallUsage(parent.parent as KtSuperTypeCallEntry, functionUsageInfo)) + } + + element is KtSimpleNameExpression && (functionPsi is KtProperty || functionPsi is KtParameter) -> + result.add(KotlinPropertyCallUsage(element)) } - - element is KtSimpleNameExpression && (functionPsi is KtProperty || functionPsi is KtParameter) -> - result.add(KotlinPropertyCallUsage(element)) } } } @@ -660,16 +668,20 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor { ) { if (originalReceiverInfo != null) return - for (usageInfo in usages) { - if (!(usageInfo is KotlinFunctionCallUsage || usageInfo is KotlinPropertyCallUsage)) continue + loop@ for (usageInfo in usages) { + if (!(usageInfo is KotlinFunctionCallUsage || usageInfo is KotlinPropertyCallUsage || usageInfo is KotlinByConventionCallUsage)) continue val callElement = usageInfo.element as? KtElement ?: continue - val parent = callElement.parent - if (parent is KtQualifiedExpression && parent.selectorExpression === callElement) { - val message = "Explicit receiver is already present in call element: " + CommonRefactoringUtil.htmlEmphasize(parent.text) - result.putValue(callElement, message) + + val elementToReport = when { + usageInfo is KotlinByConventionCallUsage -> callElement + parent is KtQualifiedExpression && parent.selectorExpression === callElement -> parent + else -> continue@loop } + + val message = "Explicit receiver is already present in call element: " + CommonRefactoringUtil.htmlEmphasize(elementToReport.text) + result.putValue(callElement, message) } } @@ -851,6 +863,10 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor { } if (beforeMethodChange) { + if (usageInfo is KotlinUsageInfo<*>) { + usageInfo.preprocessUsage() + } + if (method !is PsiMethod || initializedOriginalDescriptor) return false val descriptorWrapper = usages.firstIsInstanceOrNull() diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinByConventionCallUsage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinByConventionCallUsage.kt new file mode 100644 index 00000000000..c75cd83b62e --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinByConventionCallUsage.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +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.inspections.conventionNameCalls.ReplaceGetOrSetInspection +import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention +import org.jetbrains.kotlin.idea.intentions.conventionNameCalls.ReplaceInvokeIntention +import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeInfo +import org.jetbrains.kotlin.psi.KtCallExpression +import org.jetbrains.kotlin.psi.KtDotQualifiedExpression +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.psiUtil.getPossiblyQualifiedCallExpression +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.util.OperatorNameConventions + +class KotlinByConventionCallUsage( + expression: KtExpression, + private val callee: KotlinCallableDefinitionUsage<*> +) : KotlinUsageInfo(expression) { + private var resolvedCall: ResolvedCall<*>? = null + private var convertedCallExpression: KtCallExpression? = null + + private fun foldExpression(expression: KtDotQualifiedExpression, changeInfo: KotlinChangeInfo) { + when (changeInfo.newName) { + OperatorNameConventions.INVOKE.asString() -> { + with(ReplaceInvokeIntention()) { + if (applicabilityRange(expression) != null) { + applyTo(expression, null) + } + } + } + + OperatorNameConventions.GET.asString() -> { + with(ReplaceGetOrSetInspection()) { + if (isApplicable(expression)) { + applyTo(expression) + } + } + } + } + } + + override fun getElement(): KtExpression? { + return convertedCallExpression ?: super.getElement() + } + + override fun preprocessUsage() { + val element = element ?: return + val convertedExpression = OperatorToFunctionIntention.convert(element).first + val callExpression = convertedExpression.getPossiblyQualifiedCallExpression() ?: return + resolvedCall = callExpression.getResolvedCall(callExpression.analyze(BodyResolveMode.PARTIAL)) + convertedCallExpression = callExpression + } + + override fun processUsage(changeInfo: KotlinChangeInfo, element: KtExpression, allUsages: Array): Boolean { + val resolvedCall = resolvedCall ?: return true + val callExpression = convertedCallExpression ?: return true + val callProcessor = KotlinFunctionCallUsage(callExpression, callee, resolvedCall) + val newExpression = callProcessor.processUsageAndGetResult(changeInfo, callExpression, allUsages) as? KtExpression + val qualifiedCall = newExpression?.getQualifiedExpressionForSelectorOrThis() as? KtDotQualifiedExpression + if (qualifiedCall != null) { + foldExpression(qualifiedCall, changeInfo) + } + return true + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinCallableDefinitionUsage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinCallableDefinitionUsage.kt index 5806174e3fe..294e834f7a6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinCallableDefinitionUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinCallableDefinitionUsage.kt @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.refactoring.replaceListPsiAndKeepDelimiters import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.core.ShortenReferences.Options import org.jetbrains.kotlin.idea.refactoring.changeSignature.* +import org.jetbrains.kotlin.idea.refactoring.dropOperatorKeywordIfNecessary import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext @@ -140,6 +141,7 @@ class KotlinCallableDefinitionUsage( if (canDropOverride) { dropOverrideKeywordIfNecessary(element) } + dropOperatorKeywordIfNecessary(element) return true } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinFunctionCallUsage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinFunctionCallUsage.kt index 78a88e5abab..7c496a7bf98 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinFunctionCallUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinFunctionCallUsage.kt @@ -56,19 +56,28 @@ import java.util.* class KotlinFunctionCallUsage( element: KtCallElement, - private val callee: KotlinCallableDefinitionUsage<*> + private val callee: KotlinCallableDefinitionUsage<*>, + forcedResolvedCall: ResolvedCall<*>? = null ) : KotlinUsageInfo(element) { private val context = element.analyze(BodyResolveMode.FULL) - private val resolvedCall = element.getResolvedCall(context) + private val resolvedCall = forcedResolvedCall ?: element.getResolvedCall(context) + private val skipUnmatchedArgumentsCheck = forcedResolvedCall != null override fun processUsage(changeInfo: KotlinChangeInfo, element: KtCallElement, allUsages: Array): Boolean { - if (shouldSkipUsage(element)) return true + processUsageAndGetResult(changeInfo, element, allUsages) + return true + } + + fun processUsageAndGetResult(changeInfo: KotlinChangeInfo, element: KtCallElement, allUsages: Array): KtElement { + if (shouldSkipUsage(element)) return element + + var result: KtElement = element changeNameIfNeeded(changeInfo, element) if (element.valueArgumentList != null) { if (changeInfo.isParameterSetOrOrderChanged) { - updateArgumentsAndReceiver(changeInfo, element, allUsages) + result = updateArgumentsAndReceiver(changeInfo, element, allUsages) } else { changeArgumentNames(changeInfo, element) @@ -83,7 +92,7 @@ class KotlinFunctionCallUsage( } } - return true + return result } private fun shouldSkipUsage(element: KtCallElement): Boolean { @@ -94,6 +103,8 @@ class KotlinFunctionCallUsage( // TODO: investigate why arguments are not recorded for enum constructor call if (element is KtSuperTypeCallEntry && element.parent.parent is KtEnumEntry) return false + if (skipUnmatchedArgumentsCheck) return false + if (!resolvedCall.call.valueArguments.all{ resolvedCall.getArgumentMapping(it) is ArgumentMatch }) return true val arguments = resolvedCall.valueArguments @@ -305,7 +316,7 @@ class KotlinFunctionCallUsage( } } - private fun updateArgumentsAndReceiver(changeInfo: KotlinChangeInfo, element: KtCallElement, allUsages: Array) { + private fun updateArgumentsAndReceiver(changeInfo: KotlinChangeInfo, element: KtCallElement, allUsages: Array): KtElement { if (isPropertyJavaUsage) return updateJavaPropertyCall(changeInfo, element) val fullCallElement = element.getQualifiedExpressionForSelector() ?: element @@ -322,7 +333,7 @@ class KotlinFunctionCallUsage( val dispatchReceiver = resolvedCall?.dispatchReceiver // Do not add extension receiver to calls with explicit dispatch receiver - if (newReceiverInfo != null && fullCallElement is KtQualifiedExpression && dispatchReceiver is ExpressionReceiver) return + if (newReceiverInfo != null && fullCallElement is KtQualifiedExpression && dispatchReceiver is ExpressionReceiver) return element val newArgumentInfos = newParameters.withIndex().map { val (index, param) = it @@ -386,7 +397,7 @@ class KotlinFunctionCallUsage( if (it is KtValueArgument) addArgument(it) } - else -> return + else -> return element } } } @@ -456,6 +467,8 @@ class KotlinFunctionCallUsage( val newCallExpression = ((newElement as? KtQualifiedExpression)?.selectorExpression ?: newElement) as KtCallExpression newCallExpression.moveFunctionLiteralOutsideParentheses() } + + return newElement } private fun changeArgumentNames(changeInfo: KotlinChangeInfo, element: KtCallElement) { @@ -488,10 +501,10 @@ class KotlinFunctionCallUsage( private val SHORTEN_ARGUMENTS_OPTIONS = ShortenReferences.Options(true, true) - private fun updateJavaPropertyCall(changeInfo: KotlinChangeInfo, element: KtCallElement) { + private fun updateJavaPropertyCall(changeInfo: KotlinChangeInfo, element: KtCallElement): KtElement { val newReceiverInfo = changeInfo.receiverParameterInfo val originalReceiverInfo = changeInfo.methodDescriptor.receiver - if (newReceiverInfo == originalReceiverInfo) return + if (newReceiverInfo == originalReceiverInfo) return element val arguments = element.valueArgumentList.sure { "Argument list is expected: " + element.text } val oldArguments = element.valueArguments @@ -515,6 +528,8 @@ class KotlinFunctionCallUsage( firstArgument != null -> arguments.removeArgument(firstArgument) } + + return element } private fun getReceiverExpression(receiver: ReceiverValue, psiFactory: KtPsiFactory): KtExpression? { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinUsageInfo.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinUsageInfo.kt index 55c416b10be..f5d892a5bfd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinUsageInfo.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinUsageInfo.kt @@ -28,5 +28,7 @@ abstract class KotlinUsageInfo : UsageInfo { @Suppress("UNCHECKED_CAST") override fun getElement() = super.getElement() as T? + open fun preprocessUsage() {} + abstract fun processUsage(changeInfo: KotlinChangeInfo, element: T, allUsages: Array): Boolean } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt index ff4ffb2d088..9b3a8b3c096 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt @@ -70,6 +70,7 @@ import org.jetbrains.kotlin.idea.highlighter.markers.actualsForExpected import org.jetbrains.kotlin.idea.highlighter.markers.liftToExpected import org.jetbrains.kotlin.idea.intentions.RemoveCurlyBracesFromTemplateIntention import org.jetbrains.kotlin.idea.j2k.IdeaJavaToKotlinServices +import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar import org.jetbrains.kotlin.idea.refactoring.changeSignature.toValVar import org.jetbrains.kotlin.idea.refactoring.memberInfo.KtPsiClassWrapper @@ -85,9 +86,7 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.renderer.DescriptorRenderer -import org.jetbrains.kotlin.resolve.AnalyzingUtils -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.* import org.jetbrains.kotlin.resolve.calls.callUtil.getCallWithAssert import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode @@ -797,6 +796,15 @@ fun dropOverrideKeywordIfNecessary(element: KtNamedDeclaration) { } } +fun dropOperatorKeywordIfNecessary(element: KtNamedDeclaration) { + val callableDescriptor = element.resolveToDescriptorIfAny() as? CallableDescriptor ?: return + val diagnosticHolder = BindingTraceContext() + OperatorModifierChecker.check(element, callableDescriptor, diagnosticHolder, element.languageVersionSettings) + if (diagnosticHolder.bindingContext.diagnostics.any { it.factory == Errors.INAPPLICABLE_OPERATOR_MODIFIER }) { + element.removeModifier(KtTokens.OPERATOR_KEYWORD) + } +} + fun getQualifiedTypeArgumentList( initializer: KtExpression, context: BindingContext = initializer.analyze(BodyResolveMode.PARTIAL) diff --git a/idea/testData/refactoring/changeSignature/GetConventionAddParameterAfter.kt b/idea/testData/refactoring/changeSignature/GetConventionAddParameterAfter.kt new file mode 100644 index 00000000000..af54de61724 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionAddParameterAfter.kt @@ -0,0 +1,9 @@ +class A { + operator fun get(s: String, i: Int, b: Boolean) {} +} + +fun usage(a: A) { + a["", 42, false] + A()["", 42, false] + a.get("", 42, false) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/GetConventionAddParameterBefore.kt b/idea/testData/refactoring/changeSignature/GetConventionAddParameterBefore.kt new file mode 100644 index 00000000000..35feb05d47a --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionAddParameterBefore.kt @@ -0,0 +1,9 @@ +class A { + operator fun get(s: String, i: Int) {} +} + +fun usage(a: A) { + a["", 42] + A()["", 42] + a.get("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/GetConventionParameterToReceiverBefore.kt b/idea/testData/refactoring/changeSignature/GetConventionParameterToReceiverBefore.kt new file mode 100644 index 00000000000..35feb05d47a --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionParameterToReceiverBefore.kt @@ -0,0 +1,9 @@ +class A { + operator fun get(s: String, i: Int) {} +} + +fun usage(a: A) { + a["", 42] + A()["", 42] + a.get("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/GetConventionParameterToReceiverMessages.txt b/idea/testData/refactoring/changeSignature/GetConventionParameterToReceiverMessages.txt new file mode 100644 index 00000000000..b8f353b38d4 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionParameterToReceiverMessages.txt @@ -0,0 +1,3 @@ +Explicit receiver is already present in call element: A()["", 42] +Explicit receiver is already present in call element: a.get("", 42) +Explicit receiver is already present in call element: a["", 42] \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/GetConventionReceiverToParameterAfter.kt b/idea/testData/refactoring/changeSignature/GetConventionReceiverToParameterAfter.kt new file mode 100644 index 00000000000..b2b1e93e791 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionReceiverToParameterAfter.kt @@ -0,0 +1,9 @@ +class A + +fun get(a: A, s: String, i: Int) {} + +fun usage(a: A) { + get(a, "", 42) + get(A(), "", 42) + get(a, "", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/GetConventionReceiverToParameterBefore.kt b/idea/testData/refactoring/changeSignature/GetConventionReceiverToParameterBefore.kt new file mode 100644 index 00000000000..940a0478f57 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionReceiverToParameterBefore.kt @@ -0,0 +1,9 @@ +class A + +operator fun A.get(s: String, i: Int) {} + +fun usage(a: A) { + a["", 42] + A()["", 42] + a.get("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/GetConventionRemoveParameterAfter.kt b/idea/testData/refactoring/changeSignature/GetConventionRemoveParameterAfter.kt new file mode 100644 index 00000000000..0ada1a27a8f --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionRemoveParameterAfter.kt @@ -0,0 +1,9 @@ +class A { + operator fun get(i: Int) {} +} + +fun usage(a: A) { + a[42] + A()[42] + a.get(42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/GetConventionRemoveParameterBefore.kt b/idea/testData/refactoring/changeSignature/GetConventionRemoveParameterBefore.kt new file mode 100644 index 00000000000..35feb05d47a --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionRemoveParameterBefore.kt @@ -0,0 +1,9 @@ +class A { + operator fun get(s: String, i: Int) {} +} + +fun usage(a: A) { + a["", 42] + A()["", 42] + a.get("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/GetConventionRenameToFooAfter.kt b/idea/testData/refactoring/changeSignature/GetConventionRenameToFooAfter.kt new file mode 100644 index 00000000000..5a833f43da6 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionRenameToFooAfter.kt @@ -0,0 +1,9 @@ +class A { + fun foo(s: String, i: Int) {} +} + +fun usage(a: A) { + a.foo("", 42) + A().foo("", 42) + a.foo("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/GetConventionRenameToFooBefore.kt b/idea/testData/refactoring/changeSignature/GetConventionRenameToFooBefore.kt new file mode 100644 index 00000000000..35feb05d47a --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionRenameToFooBefore.kt @@ -0,0 +1,9 @@ +class A { + operator fun get(s: String, i: Int) {} +} + +fun usage(a: A) { + a["", 42] + A()["", 42] + a.get("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/GetConventionRenameToInvokeAfter.kt b/idea/testData/refactoring/changeSignature/GetConventionRenameToInvokeAfter.kt new file mode 100644 index 00000000000..d4fab44145e --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionRenameToInvokeAfter.kt @@ -0,0 +1,9 @@ +class A { + operator fun invoke(s: String, i: Int) {} +} + +fun usage(a: A) { + a("", 42) + A()("", 42) + a.invoke("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/GetConventionRenameToInvokeBefore.kt b/idea/testData/refactoring/changeSignature/GetConventionRenameToInvokeBefore.kt new file mode 100644 index 00000000000..35feb05d47a --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionRenameToInvokeBefore.kt @@ -0,0 +1,9 @@ +class A { + operator fun get(s: String, i: Int) {} +} + +fun usage(a: A) { + a["", 42] + A()["", 42] + a.get("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/GetConventionSwapParametersAfter.kt b/idea/testData/refactoring/changeSignature/GetConventionSwapParametersAfter.kt new file mode 100644 index 00000000000..5f0fb600f48 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionSwapParametersAfter.kt @@ -0,0 +1,9 @@ +class A { + operator fun get(i: Int, s: String) {} +} + +fun usage(a: A) { + a[42, ""] + A()[42, ""] + a.get(42, "") +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/GetConventionSwapParametersBefore.kt b/idea/testData/refactoring/changeSignature/GetConventionSwapParametersBefore.kt new file mode 100644 index 00000000000..35feb05d47a --- /dev/null +++ b/idea/testData/refactoring/changeSignature/GetConventionSwapParametersBefore.kt @@ -0,0 +1,9 @@ +class A { + operator fun get(s: String, i: Int) {} +} + +fun usage(a: A) { + a["", 42] + A()["", 42] + a.get("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionAddParameterAfter.kt b/idea/testData/refactoring/changeSignature/InvokeConventionAddParameterAfter.kt new file mode 100644 index 00000000000..51e2f50b4a1 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionAddParameterAfter.kt @@ -0,0 +1,9 @@ +class A { + operator fun invoke(s: String, i: Int, b: Boolean) {} +} + +fun usage(a: A) { + a("", 42, false) + A()("", 42, false) + a.invoke("", 42, false) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionAddParameterBefore.kt b/idea/testData/refactoring/changeSignature/InvokeConventionAddParameterBefore.kt new file mode 100644 index 00000000000..1997936d326 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionAddParameterBefore.kt @@ -0,0 +1,9 @@ +class A { + operator fun invoke(s: String, i: Int) {} +} + +fun usage(a: A) { + a("", 42) + A()("", 42) + a.invoke("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionParameterToReceiverBefore.kt b/idea/testData/refactoring/changeSignature/InvokeConventionParameterToReceiverBefore.kt new file mode 100644 index 00000000000..1997936d326 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionParameterToReceiverBefore.kt @@ -0,0 +1,9 @@ +class A { + operator fun invoke(s: String, i: Int) {} +} + +fun usage(a: A) { + a("", 42) + A()("", 42) + a.invoke("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionParameterToReceiverMessages.txt b/idea/testData/refactoring/changeSignature/InvokeConventionParameterToReceiverMessages.txt new file mode 100644 index 00000000000..6b843c61ac0 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionParameterToReceiverMessages.txt @@ -0,0 +1,3 @@ +Explicit receiver is already present in call element: A()("", 42) +Explicit receiver is already present in call element: a("", 42) +Explicit receiver is already present in call element: a.invoke("", 42) \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionReceiverToParameterAfter.kt b/idea/testData/refactoring/changeSignature/InvokeConventionReceiverToParameterAfter.kt new file mode 100644 index 00000000000..f7b4b5341d6 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionReceiverToParameterAfter.kt @@ -0,0 +1,9 @@ +class A + +fun invoke(a: A, s: String, i: Int) {} + +fun usage(a: A) { + invoke(a, "", 42) + invoke(A(), "", 42) + invoke(a, "", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionReceiverToParameterBefore.kt b/idea/testData/refactoring/changeSignature/InvokeConventionReceiverToParameterBefore.kt new file mode 100644 index 00000000000..56df90a4661 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionReceiverToParameterBefore.kt @@ -0,0 +1,9 @@ +class A + +operator fun A.invoke(s: String, i: Int) {} + +fun usage(a: A) { + a("", 42) + A()("", 42) + a.invoke("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionRemoveParameterAfter.kt b/idea/testData/refactoring/changeSignature/InvokeConventionRemoveParameterAfter.kt new file mode 100644 index 00000000000..bededb2ce80 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionRemoveParameterAfter.kt @@ -0,0 +1,9 @@ +class A { + operator fun invoke(i: Int) {} +} + +fun usage(a: A) { + a(42) + A()(42) + a.invoke(42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionRemoveParameterBefore.kt b/idea/testData/refactoring/changeSignature/InvokeConventionRemoveParameterBefore.kt new file mode 100644 index 00000000000..1997936d326 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionRemoveParameterBefore.kt @@ -0,0 +1,9 @@ +class A { + operator fun invoke(s: String, i: Int) {} +} + +fun usage(a: A) { + a("", 42) + A()("", 42) + a.invoke("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionRenameToFooAfter.kt b/idea/testData/refactoring/changeSignature/InvokeConventionRenameToFooAfter.kt new file mode 100644 index 00000000000..5a833f43da6 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionRenameToFooAfter.kt @@ -0,0 +1,9 @@ +class A { + fun foo(s: String, i: Int) {} +} + +fun usage(a: A) { + a.foo("", 42) + A().foo("", 42) + a.foo("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionRenameToFooBefore.kt b/idea/testData/refactoring/changeSignature/InvokeConventionRenameToFooBefore.kt new file mode 100644 index 00000000000..1997936d326 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionRenameToFooBefore.kt @@ -0,0 +1,9 @@ +class A { + operator fun invoke(s: String, i: Int) {} +} + +fun usage(a: A) { + a("", 42) + A()("", 42) + a.invoke("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionRenameToGetAfter.kt b/idea/testData/refactoring/changeSignature/InvokeConventionRenameToGetAfter.kt new file mode 100644 index 00000000000..19236211e3d --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionRenameToGetAfter.kt @@ -0,0 +1,9 @@ +class A { + operator fun get(s: String, i: Int) {} +} + +fun usage(a: A) { + a["", 42] + A()["", 42] + a.get("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionRenameToGetBefore.kt b/idea/testData/refactoring/changeSignature/InvokeConventionRenameToGetBefore.kt new file mode 100644 index 00000000000..1997936d326 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionRenameToGetBefore.kt @@ -0,0 +1,9 @@ +class A { + operator fun invoke(s: String, i: Int) {} +} + +fun usage(a: A) { + a("", 42) + A()("", 42) + a.invoke("", 42) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionSwapParametersAfter.kt b/idea/testData/refactoring/changeSignature/InvokeConventionSwapParametersAfter.kt new file mode 100644 index 00000000000..591f8f5aa78 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionSwapParametersAfter.kt @@ -0,0 +1,9 @@ +class A { + operator fun invoke(i: Int, s: String) {} +} + +fun usage(a: A) { + a(42, "") + A()(42, "") + a.invoke(42, "") +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/InvokeConventionSwapParametersBefore.kt b/idea/testData/refactoring/changeSignature/InvokeConventionSwapParametersBefore.kt new file mode 100644 index 00000000000..1997936d326 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/InvokeConventionSwapParametersBefore.kt @@ -0,0 +1,9 @@ +class A { + operator fun invoke(s: String, i: Int) {} +} + +fun usage(a: A) { + a("", 42) + A()("", 42) + a.invoke("", 42) +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt index c0aa65a1c2e..c7aaba45bcb 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt @@ -960,4 +960,52 @@ class KotlinChangeSignatureTest : KotlinLightCodeInsightFixtureTestCase() { fun testChangeReturnTypeToNonUnit() = doTest { newReturnTypeInfo = KotlinTypeInfo(true, BUILT_INS.intType) } + + fun testInvokeConventionRemoveParameter() = doTest { removeParameter(0) } + + fun testInvokeConventionAddParameter() = doTest { + addParameter( + KotlinParameterInfo( + originalBaseFunctionDescriptor, + -1, + "b", + KotlinTypeInfo(false, BUILT_INS.booleanType), + defaultValueForCall = KtPsiFactory(project).createExpression("false") + ) + ) + } + + fun testInvokeConventionSwapParameters() = doTest { swapParameters(0, 1) } + + fun testInvokeConventionParameterToReceiver() = doTestConflict { receiverParameterInfo = newParameters[0] } + + fun testInvokeConventionReceiverToParameter() = doTest { receiverParameterInfo = null } + + fun testInvokeConventionRenameToFoo() = doTest { newName = "foo" } + + fun testInvokeConventionRenameToGet() = doTest { newName = "get" } + + fun testGetConventionRemoveParameter() = doTest { removeParameter(0) } + + fun testGetConventionAddParameter() = doTest { + addParameter( + KotlinParameterInfo( + originalBaseFunctionDescriptor, + -1, + "b", + KotlinTypeInfo(false, BUILT_INS.booleanType), + defaultValueForCall = KtPsiFactory(project).createExpression("false") + ) + ) + } + + fun testGetConventionSwapParameters() = doTest { swapParameters(0, 1) } + + fun testGetConventionParameterToReceiver() = doTestConflict { receiverParameterInfo = newParameters[0] } + + fun testGetConventionReceiverToParameter() = doTest { receiverParameterInfo = null } + + fun testGetConventionRenameToFoo() = doTest { newName = "foo" } + + fun testGetConventionRenameToInvoke() = doTest { newName = "invoke" } } \ No newline at end of file