Add support for mixed named arguments to parameter info popup

Don't display parameter info in square brackets if the caller doesn't
have to use a named argument. This makes the parameter info popup
reflect the new capability introduced with
MixedNamedArgumentsInTheirOwnPosition.

^KT-41645 Fixed
This commit is contained in:
cketti
2020-09-03 12:30:27 +02:00
committed by Vladimir Dolzhenko
parent af6e744b65
commit 91c021c699
4 changed files with 58 additions and 5 deletions

View File

@@ -25,14 +25,17 @@ import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.ui.Gray
import com.intellij.ui.JBColor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.FrontendInternals
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.completion.canBeUsedWithoutNameInCall
import org.jetbrains.kotlin.idea.core.OptionalParametersHelper
import org.jetbrains.kotlin.idea.core.resolveCandidates
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.util.ShadowedDeclarationsFilter
@@ -213,6 +216,9 @@ abstract class KotlinParameterInfoWithCallHandlerBase<TArgumentList : KtElement,
if (!argumentListClass.java.isInstance(context.parameterOwner)) return false
val call = itemToShow.call ?: return false
val supportsMixedNamedArgumentsInTheirOwnPosition =
call.callElement.languageVersionSettings.supportsFeature(LanguageFeature.MixedNamedArgumentsInTheirOwnPosition)
@Suppress("UNCHECKED_CAST")
val argumentList = context.parameterOwner as TArgumentList
@@ -230,6 +236,7 @@ abstract class KotlinParameterInfoWithCallHandlerBase<TArgumentList : KtElement,
val text = buildString {
val usedParameterIndices = HashSet<Int>()
var namedMode = false
var argumentIndex = 0
if (call.callType == Call.CallType.ARRAY_SET_METHOD) {
// for set-operator the last parameter is used for the value assigned
@@ -238,7 +245,9 @@ abstract class KotlinParameterInfoWithCallHandlerBase<TArgumentList : KtElement,
val includeParameterNames = !substitutedDescriptor.hasSynthesizedParameterNames()
fun appendParameter(parameter: ValueParameterDescriptor) {
fun appendParameter(parameter: ValueParameterDescriptor, named: Boolean = false) {
argumentIndex++
if (length > 0) {
append(", ")
}
@@ -248,7 +257,7 @@ abstract class KotlinParameterInfoWithCallHandlerBase<TArgumentList : KtElement,
boldStartOffset = length
}
append(renderParameter(parameter, includeParameterNames, namedMode, project))
append(renderParameter(parameter, includeParameterNames, named || namedMode, project))
if (highlightParameter) {
boldEndOffset = length
@@ -260,15 +269,20 @@ abstract class KotlinParameterInfoWithCallHandlerBase<TArgumentList : KtElement,
val parameter = argumentToParameter(argument) ?: continue
if (!usedParameterIndices.add(parameter.index)) continue
if (argument.isNamed()) {
if (argument.isNamed() &&
!(supportsMixedNamedArgumentsInTheirOwnPosition && argument.canBeUsedWithoutNameInCall(itemToShow))
) {
namedMode = true
}
appendParameter(parameter)
appendParameter(parameter, argument.isNamed())
}
for (parameter in substitutedDescriptor.valueParameters) {
if (parameter.index !in usedParameterIndices) {
if (argumentIndex != parameter.index) {
namedMode = true
}
appendParameter(parameter)
}
}
@@ -521,6 +535,9 @@ abstract class KotlinParameterInfoWithCallHandlerBase<TArgumentList : KtElement,
private fun ValueArgument.hasError(bindingContext: BindingContext) =
getArgumentExpression()?.let { bindingContext.getType(it) }?.isError ?: true
private fun ValueArgument.canBeUsedWithoutNameInCall(callInfo: CallInfo) =
this is KtValueArgument && this.canBeUsedWithoutNameInCall(callInfo.resolvedCall as ResolvedCall<out CallableDescriptor>)
// we should not compare descriptors directly because partial resolve is involved
private fun descriptorsEqual(descriptor1: FunctionDescriptor, descriptor2: FunctionDescriptor): Boolean {
if (descriptor1.original == descriptor2.original) return true

View File

@@ -0,0 +1,12 @@
open class A(x: Int) {
fun m(x: Boolean, y: Int) = 1
fun m(x: Boolean, y: Int, z: Int) = 2
fun d(x: Int) {
m(false, y = 23<caret>)
}
}
/*
Text: (x: Boolean, <highlight>[y: Int]</highlight>), Disabled: false, Strikeout: false, Green: true
Text: (x: Boolean, <highlight>[y: Int]</highlight>, z: Int), Disabled: false, Strikeout: false, Green: false
*/

View File

@@ -0,0 +1,14 @@
// COMPILER_ARGUMENTS: -XXLanguage:-MixedNamedArgumentsInTheirOwnPosition
open class A(x: Int) {
fun m(x: Boolean, y: Int) = 1
fun m(x: Boolean, y: Int, z: Int) = 2
fun d(x: Int) {
m(false, y = 23<caret>)
}
}
/*
Text: (x: Boolean, <highlight>[y: Int]</highlight>), Disabled: false, Strikeout: false, Green: true
Text: (x: Boolean, <highlight>[y: Int]</highlight>, [z: Int]), Disabled: false, Strikeout: false, Green: false
*/

View File

@@ -196,6 +196,16 @@ public class ParameterInfoTestGenerated extends AbstractParameterInfoTest {
runTest("idea/testData/parameterInfo/functionCall/LocalFunctionBug.kt");
}
@TestMetadata("MixedNamedArguments.kt")
public void testMixedNamedArguments() throws Exception {
runTest("idea/testData/parameterInfo/functionCall/MixedNamedArguments.kt");
}
@TestMetadata("MixedNamedArguments2.kt")
public void testMixedNamedArguments2() throws Exception {
runTest("idea/testData/parameterInfo/functionCall/MixedNamedArguments2.kt");
}
@TestMetadata("NamedAndDefaultParameter.kt")
public void testNamedAndDefaultParameter() throws Exception {
runTest("idea/testData/parameterInfo/functionCall/NamedAndDefaultParameter.kt");