From d599322503aa8ba9ae90e7de8fc51f9677659a72 Mon Sep 17 00:00:00 2001 From: Vladimir Dolzhenko Date: Thu, 14 Nov 2019 21:37:15 +0100 Subject: [PATCH] Calculate method parameter hint info in updateParameterInfo method (with a progress till 201) Fixed #EA-216268 --- .../KotlinFunctionParameterInfoHandler.kt | 204 +++++++++++++----- .../AbstractParameterInfoTest.kt | 2 +- .../AbstractParameterInfoTest.kt.193 | 2 +- .../MockUpdateParameterInfoContext.java | 7 +- 4 files changed, 154 insertions(+), 61 deletions(-) diff --git a/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinFunctionParameterInfoHandler.kt b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinFunctionParameterInfoHandler.kt index 66a0c94f6d7..4151bab227c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinFunctionParameterInfoHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/parameterInfo/KotlinFunctionParameterInfoHandler.kt @@ -19,6 +19,9 @@ package org.jetbrains.kotlin.idea.parameterInfo import com.intellij.codeInsight.CodeInsightBundle import com.intellij.codeInsight.lookup.LookupElement import com.intellij.lang.parameterInfo.* +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.runReadAction +import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.Project import com.intellij.psi.impl.source.tree.LeafPsiElement import com.intellij.psi.util.PsiTreeUtil @@ -48,6 +51,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue @@ -97,7 +101,7 @@ class KotlinArrayAccessParameterInfoHandler : abstract class KotlinParameterInfoWithCallHandlerBase( private val argumentListClass: KClass, private val argumentClass: KClass -) : ParameterInfoHandlerWithTabActionSupport { +) : ParameterInfoHandlerWithTabActionSupport { companion object { @JvmField @@ -169,7 +173,7 @@ abstract class KotlinParameterInfoWithCallHandlerBase? = null, + var arguments: List = emptyList(), + var dummyArgument: ValueArgument? = null, + var dummyResolvedCall: ResolvedCall? = null, + var isResolvedToDescriptor: Boolean = false, + var isGreyArgumentIndex: Int = -1 + ) + + private fun resolveCallInfo( + info: CallInfo, call: Call, - overload: FunctionDescriptor, - currentArgumentIndex: Int, bindingContext: BindingContext, resolutionFacade: ResolutionFacade - ): SignatureInfo? { - if (currentArgumentIndex == 0 && call.valueArguments.isEmpty() && overload.valueParameters.isEmpty()) { - return SignatureInfo(overload, { null }, null, isGrey = false) - } - + ) { + val overload = info.overload ?: return val isArraySetMethod = call.callType == Call.CallType.ARRAY_SET_METHOD - val arguments = call.valueArguments.let { args -> + fun calculateArgument(c: Call) = c.valueArguments.let { args -> // For array set method call, we're only interested in the arguments in brackets which are all except the last one - if (isArraySetMethod) args.dropLast(1) else args + if (c.callType == Call.CallType.ARRAY_SET_METHOD) args.dropLast(1) else args } - assert(arguments.size >= currentArgumentIndex) + val arguments = calculateArgument(call) - val callToUse: Call - val currentArgument: ValueArgument - if (arguments.size > currentArgumentIndex) { - currentArgument = arguments[currentArgumentIndex] - callToUse = call - } else { - // add dummy current argument if we don't have one - currentArgument = object : ValueArgument { - override fun getArgumentExpression(): KtExpression? = null - override fun getArgumentName(): ValueArgumentName? = null - override fun isNamed(): Boolean = false - override fun asElement(): KtElement = call.callElement // is a hack but what to do? - override fun getSpreadElement(): LeafPsiElement? = null - override fun isExternal() = false - } - callToUse = object : DelegatingCall(call) { - val argumentsWithCurrent = - arguments + currentArgument + - // For array set method call, also add the argument in the right-hand side - (if (isArraySetMethod) listOf(call.valueArguments.last()) else listOf()) + val resolvedCall = resolvedCall(call, bindingContext, resolutionFacade, overload) ?: return - override fun getValueArguments() = argumentsWithCurrent - override fun getFunctionLiteralArguments() = emptyList() - override fun getValueArgumentList(): KtValueArgumentList? = null - } + // add dummy current argument if we don't have one + val dummyArgument = object : ValueArgument { + override fun getArgumentExpression(): KtExpression? = null + override fun getArgumentName(): ValueArgumentName? = null + override fun isNamed(): Boolean = false + override fun asElement(): KtElement = call.callElement // is a hack but what to do? + override fun getSpreadElement(): LeafPsiElement? = null + override fun isExternal() = false } - val candidates = callToUse.resolveCandidates(bindingContext, resolutionFacade) + val dummyResolvedCall = + dummyResolvedCall(call, arguments, dummyArgument, isArraySetMethod, bindingContext, resolutionFacade, overload) + + val resultingDescriptor = resolvedCall.resultingDescriptor + + val resolvedToDescriptor = isResolvedToDescriptor(call, resultingDescriptor, bindingContext) + + // grey out if not all arguments are matched + val isGreyArgumentIndex = arguments.indexOfFirst { argument -> + resolvedCall.getArgumentMapping(argument).isError() && + !argument.hasError(bindingContext) /* ignore arguments that have error type */ + } + + with(info) { + this.call = call + this.resolvedCall = resolvedCall + this.arguments = arguments + this.dummyArgument = dummyArgument + this.dummyResolvedCall = dummyResolvedCall + this.isResolvedToDescriptor = resolvedToDescriptor + this.isGreyArgumentIndex = isGreyArgumentIndex + } + } + + private fun dummyResolvedCall( + call: Call, + arguments: List, + dummyArgument: ValueArgument, + isArraySetMethod: Boolean, + bindingContext: BindingContext, + resolutionFacade: ResolutionFacade, + overload: FunctionDescriptor + ): ResolvedCall? { + val callToUse = object : DelegatingCall(call) { + val argumentsWithCurrent = + arguments + dummyArgument + + // For array set method call, also add the argument in the right-hand side + (if (isArraySetMethod) listOf(call.valueArguments.last()) else listOf()) + + override fun getValueArguments() = argumentsWithCurrent + override fun getFunctionLiteralArguments() = emptyList() + override fun getValueArgumentList(): KtValueArgumentList? = null + } + + return resolvedCall(callToUse, bindingContext, resolutionFacade, overload) + } + + private fun resolvedCall( + call: Call, + bindingContext: BindingContext, + resolutionFacade: ResolutionFacade, + overload: FunctionDescriptor + ): ResolvedCall? { + val candidates = call.resolveCandidates(bindingContext, resolutionFacade) + // First try to find strictly matching descriptor, then one with the same declaration. // The second way is needed for the case when the descriptor was invalidated and new one has been built. // See testLocalFunctionBug(). - val resolvedCall = candidates.firstOrNull { it.resultingDescriptor.original == overload.original } + + return candidates.firstOrNull { it.resultingDescriptor.original == overload.original } ?: candidates.firstOrNull { descriptorsEqual(it.resultingDescriptor, overload) } - ?: return null - val resultingDescriptor = resolvedCall.resultingDescriptor + ?: null + } + + private fun matchCallWithSignature( + info: CallInfo, + currentArgumentIndex: Int + ): SignatureInfo? { + val call = info.call ?: return null + val resolvedCall = info.resolvedCall ?: return null + if (currentArgumentIndex == 0 && call.valueArguments.isEmpty() && resolvedCall.resultingDescriptor.valueParameters.isEmpty()) { + return SignatureInfo(resolvedCall.resultingDescriptor, { null }, null, isGrey = false) + } + + val arguments = info.arguments + + assert(arguments.size >= currentArgumentIndex) { + "currentArgumentIndex: $currentArgumentIndex has to be not more than number of arguments ${arguments.size}" + } + + val callToUse: ResolvedCall + val currentArgument = if (arguments.size > currentArgumentIndex) { + callToUse = resolvedCall + arguments[currentArgumentIndex] + } else { + callToUse = info.dummyResolvedCall ?: return null + info.dummyArgument ?: return null + } + + val resultingDescriptor = callToUse.resultingDescriptor fun argumentToParameter(argument: ValueArgument): ValueParameterDescriptor? { - return (resolvedCall.getArgumentMapping(argument) as? ArgumentMatch)?.valueParameter + return (callToUse.getArgumentMapping(argument) as? ArgumentMatch)?.valueParameter } val highlightParameterIndex = argumentToParameter(currentArgument)?.index @@ -428,10 +521,7 @@ abstract class KotlinParameterInfoWithCallHandlerBase - resolvedCall.getArgumentMapping(argument).isError() && - !argument.hasError(bindingContext) /* ignore arguments that have error type */ - } + val isGrey = info.isGreyArgumentIndex in 0 until currentArgumentIndex return SignatureInfo(resultingDescriptor, ::argumentToParameter, highlightParameterIndex, isGrey) } diff --git a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractParameterInfoTest.kt b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractParameterInfoTest.kt index 8c65a47df91..614b33cabbe 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractParameterInfoTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractParameterInfoTest.kt @@ -76,7 +76,7 @@ abstract class AbstractParameterInfoTest : LightCodeInsightFixtureTestCase() { } //to update current parameter index - val updateContext = MockUpdateParameterInfoContext(file, myFixture) + val updateContext = MockUpdateParameterInfoContext(file, myFixture, mockCreateParameterInfoContext) val elementForUpdating = handler.findElementForUpdatingParameterInfo(updateContext) if (elementForUpdating != null) { handler.updateParameterInfo(elementForUpdating, updateContext) diff --git a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractParameterInfoTest.kt.193 b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractParameterInfoTest.kt.193 index ec81a55bb11..f1b74db5f12 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractParameterInfoTest.kt.193 +++ b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/AbstractParameterInfoTest.kt.193 @@ -76,7 +76,7 @@ abstract class AbstractParameterInfoTest : LightCodeInsightFixtureTestCase() { } //to update current parameter index - val updateContext = MockUpdateParameterInfoContext(file, myFixture) + val updateContext = MockUpdateParameterInfoContext(file, myFixture, mockCreateParameterInfoContext) val elementForUpdating = handler.findElementForUpdatingParameterInfo(updateContext) if (elementForUpdating != null) { handler.updateParameterInfo(elementForUpdating, updateContext) diff --git a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/MockUpdateParameterInfoContext.java b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/MockUpdateParameterInfoContext.java index c15e75e5ab2..7ad27ed20b1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/MockUpdateParameterInfoContext.java +++ b/idea/tests/org/jetbrains/kotlin/idea/parameterInfo/MockUpdateParameterInfoContext.java @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.idea.parameterInfo; +import com.intellij.lang.parameterInfo.CreateParameterInfoContext; import com.intellij.lang.parameterInfo.UpdateParameterInfoContext; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; @@ -19,10 +20,12 @@ public class MockUpdateParameterInfoContext implements UpdateParameterInfoContex private int myCurrentParameter = -1; private PsiFile myFile; private JavaCodeInsightTestFixture myFixture; + private CreateParameterInfoContext createParameterInfoContext; - MockUpdateParameterInfoContext(PsiFile file, JavaCodeInsightTestFixture fixture) { + MockUpdateParameterInfoContext(PsiFile file, JavaCodeInsightTestFixture fixture, CreateParameterInfoContext context) { myFile = file; myFixture = fixture; + createParameterInfoContext = context; } @@ -73,7 +76,7 @@ public class MockUpdateParameterInfoContext implements UpdateParameterInfoContex @Override public Object[] getObjectsToView() { - return ArrayUtil.EMPTY_OBJECT_ARRAY; + return createParameterInfoContext.getItemsToShow(); } @Override