From 88cd1295494cf691d47b4556bf31f67cc20d77bf Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 26 Mar 2020 14:40:07 +0200 Subject: [PATCH] Fixed other parameter references broken in new parameter values in conventional Change Signature --- .../changeSignature/KotlinParameterInfo.kt | 126 ++++++++---------- .../NewParamValueRefsOtherParamAfter.kt | 7 + .../NewParamValueRefsOtherParamBefore.kt | 7 + .../KotlinChangeSignatureTest.kt | 9 ++ 4 files changed, 76 insertions(+), 73 deletions(-) create mode 100644 idea/testData/refactoring/changeSignature/NewParamValueRefsOtherParamAfter.kt create mode 100644 idea/testData/refactoring/changeSignature/NewParamValueRefsOtherParamBefore.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinParameterInfo.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinParameterInfo.kt index c392a3f5ba5..01a6d7909e9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinParameterInfo.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinParameterInfo.kt @@ -40,86 +40,66 @@ class KotlinParameterInfo @JvmOverloads constructor( ) : ParameterInfo { var currentTypeInfo: KotlinTypeInfo = originalTypeInfo - val defaultValueParameterReferences: Map + val defaultValueParameterReferences: Map by lazy { collectDefaultValueParameterReferences(defaultValueForCall) } - init { - val file = defaultValueForCall?.containingFile as? KtFile - defaultValueParameterReferences = - if (defaultValueForCall != null && file != null && (file.isPhysical || file.analysisContext != null)) { - val project = file.project - val map = LinkedHashMap() + private fun collectDefaultValueParameterReferences(defaultValueForCall: KtExpression?): Map { + val file = defaultValueForCall?.containingFile as? KtFile ?: return emptyMap() + if (!file.isPhysical && file.analysisContext == null) return emptyMap() - defaultValueForCall!!.accept( - object : KtTreeVisitorVoid() { - private fun selfParameterOrNull(parameter: DeclarationDescriptor?): ValueParameterDescriptor? { - return if (parameter is ValueParameterDescriptor && - compareDescriptors(project, parameter.containingDeclaration, callableDescriptor) - ) { - parameter - } else null - } + val project = file.project + val map = LinkedHashMap() - private fun selfReceiverOrNull(receiverDescriptor: DeclarationDescriptor?): DeclarationDescriptor? { - if (compareDescriptors( - project, - receiverDescriptor, - callableDescriptor.extensionReceiverParameter?.containingDeclaration - ) - ) { - return receiverDescriptor - } - if (compareDescriptors( - project, - receiverDescriptor, - callableDescriptor.dispatchReceiverParameter?.containingDeclaration - ) - ) { - return receiverDescriptor - } - return null - } + defaultValueForCall.accept( + object : KtTreeVisitorVoid() { + private fun selfParameterOrNull(parameter: DeclarationDescriptor?): ValueParameterDescriptor? { + return (parameter as? ValueParameterDescriptor) + ?.takeIf { compareDescriptors(project, parameter.containingDeclaration, callableDescriptor) } + } - private fun selfReceiverOrNull(receiver: ImplicitReceiver?): DeclarationDescriptor? { - return selfReceiverOrNull(receiver?.declarationDescriptor) - } - - private fun getRelevantDescriptor( - expression: KtSimpleNameExpression, - ref: KtReference - ): DeclarationDescriptor? { - val context = expression.analyze(BodyResolveMode.PARTIAL) - - val descriptor = ref.resolveToDescriptors(context).singleOrNull() - if (descriptor is ValueParameterDescriptor) return selfParameterOrNull(descriptor) - - if (descriptor is PropertyDescriptor && callableDescriptor is ConstructorDescriptor) { - val parameter = DescriptorToSourceUtils.getSourceFromDescriptor(descriptor) as? KtParameter - return parameter?.let { selfParameterOrNull(context[BindingContext.VALUE_PARAMETER, it]) } - } - - val resolvedCall = expression.getResolvedCall(context) ?: return null - (resolvedCall.resultingDescriptor as? ReceiverParameterDescriptor)?.let { - return if (selfReceiverOrNull(it.containingDeclaration) != null) it else null - } - - selfReceiverOrNull(resolvedCall.extensionReceiver as? ImplicitReceiver)?.let { return it } - selfReceiverOrNull(resolvedCall.dispatchReceiver as? ImplicitReceiver)?.let { return it } - - return null - } - - override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) { - val ref = expression.mainReference - val descriptor = getRelevantDescriptor(expression, ref) ?: return - map[ref] = descriptor - } + private fun selfReceiverOrNull(receiverDescriptor: DeclarationDescriptor?): DeclarationDescriptor? { + if (compareDescriptors(project, receiverDescriptor, callableDescriptor.extensionReceiverParameter?.containingDeclaration)) { + return receiverDescriptor } - ) + if (compareDescriptors(project, receiverDescriptor, callableDescriptor.dispatchReceiverParameter?.containingDeclaration)) { + return receiverDescriptor + } + return null + } - map - } else { - emptyMap() + private fun selfReceiverOrNull(receiver: ImplicitReceiver?): DeclarationDescriptor? { + return selfReceiverOrNull(receiver?.declarationDescriptor) + } + + private fun getRelevantDescriptor(expression: KtSimpleNameExpression, ref: KtReference): DeclarationDescriptor? { + val context = expression.analyze(BodyResolveMode.PARTIAL) + + val descriptor = ref.resolveToDescriptors(context).singleOrNull() + if (descriptor is ValueParameterDescriptor) return selfParameterOrNull(descriptor) + + if (descriptor is PropertyDescriptor && callableDescriptor is ConstructorDescriptor) { + val parameter = DescriptorToSourceUtils.getSourceFromDescriptor(descriptor) as? KtParameter + return parameter?.let { selfParameterOrNull(context[BindingContext.VALUE_PARAMETER, it]) } + } + + val resolvedCall = expression.getResolvedCall(context) ?: return null + (resolvedCall.resultingDescriptor as? ReceiverParameterDescriptor)?.let { + return if (selfReceiverOrNull(it.containingDeclaration) != null) it else null + } + + selfReceiverOrNull(resolvedCall.extensionReceiver as? ImplicitReceiver)?.let { return it } + selfReceiverOrNull(resolvedCall.dispatchReceiver as? ImplicitReceiver)?.let { return it } + + return null + } + + override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) { + val ref = expression.mainReference + val descriptor = getRelevantDescriptor(expression, ref) ?: return + map[ref] = descriptor + } } + ) + return map } override fun getOldIndex(): Int = originalIndex diff --git a/idea/testData/refactoring/changeSignature/NewParamValueRefsOtherParamAfter.kt b/idea/testData/refactoring/changeSignature/NewParamValueRefsOtherParamAfter.kt new file mode 100644 index 00000000000..df818c20ebd --- /dev/null +++ b/idea/testData/refactoring/changeSignature/NewParamValueRefsOtherParamAfter.kt @@ -0,0 +1,7 @@ +fun foo(p1: Int, p2: Int) { +} + +fun bar() { + foo(1, 1 * 1) + foo(2, 2 * 2) +} \ No newline at end of file diff --git a/idea/testData/refactoring/changeSignature/NewParamValueRefsOtherParamBefore.kt b/idea/testData/refactoring/changeSignature/NewParamValueRefsOtherParamBefore.kt new file mode 100644 index 00000000000..a5558d04e99 --- /dev/null +++ b/idea/testData/refactoring/changeSignature/NewParamValueRefsOtherParamBefore.kt @@ -0,0 +1,7 @@ +fun foo(p1: Int) { +} + +fun bar() { + foo(1) + foo(2) +} \ 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 4f6c94e5f89..ee08e6fcb4f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureTest.kt @@ -1599,4 +1599,13 @@ class KotlinChangeSignatureTest : KotlinLightCodeInsightFixtureTestCase() { fun testRemoveLambdaParameter2() { doTest { removeParameter(0) } } + + fun testNewParamValueRefsOtherParam() { + doTest { + val parameterInfo = KotlinParameterInfo(originalBaseFunctionDescriptor, -1, "p2", KotlinTypeInfo(false, BUILT_INS.intType)) + val codeFragment = KtPsiFactory(project).createExpressionCodeFragment("p1 * p1", context) + parameterInfo.defaultValueForCall = codeFragment.getContentElement()!! + addParameter(parameterInfo) + } + } }