mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
KotlinHighLevelFunctionParameterInfoHandler: Disable candidates when there are arguments before the current that don't match the parameter type.
GitOrigin-RevId: 412eb2043c3c4541b9e5785980cde2fd5ed90476
This commit is contained in:
committed by
intellij-monorepo-bot
parent
77c2ee6c5b
commit
0ce5fa8955
+44
-15
@@ -49,7 +49,7 @@ class KotlinHighLevelLambdaParameterInfoHandler :
|
||||
|
||||
override fun getArgumentListAllowedParentClasses() = setOf(KtLambdaArgument::class.java)
|
||||
|
||||
override fun getParameterIndex(context: UpdateParameterInfoContext, argumentList: KtLambdaArgument): Int {
|
||||
override fun getCurrentArgumentIndex(context: UpdateParameterInfoContext, argumentList: KtLambdaArgument): Int {
|
||||
val size = (argumentList.parent as? KtCallElement)?.valueArguments?.size ?: 1
|
||||
return size - 1
|
||||
}
|
||||
@@ -131,8 +131,8 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K
|
||||
if (context.parameterOwner !== argumentList) {
|
||||
context.removeHint()
|
||||
}
|
||||
val parameterIndex = getParameterIndex(context, argumentList)
|
||||
context.setCurrentParameter(parameterIndex)
|
||||
val currentArgumentIndex = getCurrentArgumentIndex(context, argumentList)
|
||||
context.setCurrentParameter(currentArgumentIndex)
|
||||
|
||||
val callElement = argumentList.parent as? KtElement ?: return
|
||||
analyse(callElement) {
|
||||
@@ -191,6 +191,20 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K
|
||||
}
|
||||
}
|
||||
|
||||
var hasTypeMismatchBeforeCurrent = false
|
||||
for ((index, argument) in arguments.withIndex()) {
|
||||
if (index >= currentArgumentIndex) break
|
||||
val parameterForArgument = argumentMapping[argument] ?: continue
|
||||
if (parameterForArgument == setValueParameter) continue
|
||||
|
||||
val argumentType = argument.getKtType() ?: error("Argument should have a KtType")
|
||||
val parameterType = parameterForArgument.annotatedType.type
|
||||
if (argumentType.isNotSubTypeOf(parameterType)) {
|
||||
hasTypeMismatchBeforeCurrent = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This should be changed when there are multiple candidates available; need to know which one the call is resolved to
|
||||
val isCallResolvedToCandidate = candidates.size == 1
|
||||
|
||||
@@ -201,13 +215,14 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K
|
||||
argumentToParameterIndex,
|
||||
valueParameters.size,
|
||||
parameterIndexToText,
|
||||
isCallResolvedToCandidate
|
||||
isCallResolvedToCandidate,
|
||||
hasTypeMismatchBeforeCurrent
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun getParameterIndex(context: UpdateParameterInfoContext, argumentList: TArgumentList): Int {
|
||||
protected open fun getCurrentArgumentIndex(context: UpdateParameterInfoContext, argumentList: TArgumentList): Int {
|
||||
val offset = context.offset
|
||||
return argumentList.allChildren
|
||||
.takeWhile { it.startOffset < offset }
|
||||
@@ -280,7 +295,8 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K
|
||||
*
|
||||
* `setupUIComponentPresentation()` is called with `disabled = true` when any of the following are true:
|
||||
* 1. If the argument on the cursor does NOT map to a parameter (e.g., on N-th argument but there are < N parameters),
|
||||
* 2. If any of the arguments before the cursor do NOT match the type of the corresponding parameter.
|
||||
* 2. If any of the arguments before the cursor do NOT match the type of the corresponding parameter (ignoring arguments with type
|
||||
* errors, e.g., unresolved) or do NOT map to a parameter (e.g., named argument with an unknown name).
|
||||
* 3. If the cursor is after a trailing comma with no argument, AND LanguageFeature.TrailingCommas is DISABLED, AND there are
|
||||
* already enough arguments in the call. (We assume the user is about to enter an argument in that position.)
|
||||
*
|
||||
@@ -298,7 +314,8 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K
|
||||
if (currentArgumentIndex < 0) return false
|
||||
|
||||
val callInfo = itemToShow.callInfo ?: return false
|
||||
val (callElement, valueArguments, arguments, argumentMapping, valueParameterCount, parameterIndexToText, isCallResolvedToCandidate) = callInfo
|
||||
val (callElement, valueArguments, arguments, argumentToParameterIndex, valueParameterCount, parameterIndexToText,
|
||||
isCallResolvedToCandidate, hasTypeMismatchBeforeCurrent) = callInfo
|
||||
|
||||
val supportsMixedNamedArgumentsInTheirOwnPosition =
|
||||
callElement.languageVersionSettings.supportsFeature(LanguageFeature.MixedNamedArgumentsInTheirOwnPosition)
|
||||
@@ -316,6 +333,7 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K
|
||||
var highlightStartOffset = -1
|
||||
var highlightEndOffset = -1
|
||||
var isDisabledBeforeHighlight = false
|
||||
var hasUnmappedArgumentBeforeCurrent = false
|
||||
val usedParameterIndices = HashSet<Int>()
|
||||
val text = buildString {
|
||||
var namedMode = false
|
||||
@@ -363,7 +381,14 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K
|
||||
|
||||
if (valueArguments != null) {
|
||||
for (valueArgument in valueArguments) {
|
||||
val parameterIndex = argumentMapping[valueArgument.getArgumentExpression()] ?: continue
|
||||
val parameterIndex = argumentToParameterIndex[valueArgument.getArgumentExpression()]
|
||||
if (parameterIndex == null) {
|
||||
if (argumentIndex < currentArgumentIndex) {
|
||||
hasUnmappedArgumentBeforeCurrent = true
|
||||
}
|
||||
argumentIndex++
|
||||
continue
|
||||
}
|
||||
if (!usedParameterIndices.add(parameterIndex)) continue
|
||||
|
||||
if (valueArgument.isNamed() &&
|
||||
@@ -380,7 +405,14 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K
|
||||
} else {
|
||||
// This is for array get/set calls which don't have KtValueArguments.
|
||||
for (argument in arguments) {
|
||||
val parameterIndex = argumentMapping[argument] ?: continue
|
||||
val parameterIndex = argumentToParameterIndex[argument]
|
||||
if (parameterIndex == null) {
|
||||
if (argumentIndex <= currentArgumentIndex) {
|
||||
hasUnmappedArgumentBeforeCurrent = true
|
||||
}
|
||||
argumentIndex++
|
||||
continue
|
||||
}
|
||||
if (!usedParameterIndices.add(parameterIndex)) continue
|
||||
|
||||
val shouldHighlight = argumentIndex == currentArgumentIndex
|
||||
@@ -417,11 +449,7 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K
|
||||
val afterTrailingComma = arguments.isNotEmpty() && currentArgumentIndex == arguments.size
|
||||
val tooManyArgs = allParametersUsed && ((!supportsTrailingCommas && afterTrailingComma) || arguments.size > valueParameterCount)
|
||||
|
||||
// TODO: Also disable if not all arguments before the current are matched. Need to check this in updateParameterInfo()
|
||||
// See UpdateInTyping.kt tests
|
||||
val hasMismatchBeforeCurrent = false
|
||||
|
||||
val isDisabled = tooManyArgs || hasMismatchBeforeCurrent
|
||||
val isDisabled = tooManyArgs || hasTypeMismatchBeforeCurrent || hasUnmappedArgumentBeforeCurrent
|
||||
|
||||
context.setupUIComponentPresentation(
|
||||
text,
|
||||
@@ -443,7 +471,8 @@ abstract class KotlinHighLevelParameterInfoWithCallHandlerBase<TArgumentList : K
|
||||
val argumentToParameterIndex: LinkedHashMap<KtExpression, Int>,
|
||||
val valueParameterCount: Int,
|
||||
val parameterIndexToText: Map<Int, String>,
|
||||
val isCallResolvedToCandidate: Boolean
|
||||
val isCallResolvedToCandidate: Boolean,
|
||||
val hasTypeMismatchBeforeCurrent: Boolean,
|
||||
)
|
||||
|
||||
data class CandidateInfo(
|
||||
|
||||
+30
@@ -402,6 +402,36 @@ public abstract class FirParameterInfoTestGenerated extends AbstractFirParameter
|
||||
runTest("../idea/tests/testData/parameterInfo/functionCall/TypeInference.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UnmappedAfterCurrent.kt")
|
||||
public void testUnmappedAfterCurrent() throws Exception {
|
||||
runTest("../idea/tests/testData/parameterInfo/functionCall/UnmappedAfterCurrent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UnmappedBeforeCurrent.kt")
|
||||
public void testUnmappedBeforeCurrent() throws Exception {
|
||||
runTest("../idea/tests/testData/parameterInfo/functionCall/UnmappedBeforeCurrent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UnmappedCurrent.kt")
|
||||
public void testUnmappedCurrent() throws Exception {
|
||||
runTest("../idea/tests/testData/parameterInfo/functionCall/UnmappedCurrent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UnresolvedAfterCurrent.kt")
|
||||
public void testUnresolvedAfterCurrent() throws Exception {
|
||||
runTest("../idea/tests/testData/parameterInfo/functionCall/UnresolvedAfterCurrent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UnresolvedBeforeCurrent.kt")
|
||||
public void testUnresolvedBeforeCurrent() throws Exception {
|
||||
runTest("../idea/tests/testData/parameterInfo/functionCall/UnresolvedBeforeCurrent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UnresolvedCurrent.kt")
|
||||
public void testUnresolvedCurrent() throws Exception {
|
||||
runTest("../idea/tests/testData/parameterInfo/functionCall/UnresolvedCurrent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UpdateOnTyping.kt")
|
||||
public void testUpdateOnTyping() throws Exception {
|
||||
runTest("../idea/tests/testData/parameterInfo/functionCall/UpdateOnTyping.kt");
|
||||
|
||||
+30
@@ -402,6 +402,36 @@ public abstract class ParameterInfoTestGenerated extends AbstractParameterInfoTe
|
||||
runTest("testData/parameterInfo/functionCall/TypeInference.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UnmappedAfterCurrent.kt")
|
||||
public void testUnmappedAfterCurrent() throws Exception {
|
||||
runTest("testData/parameterInfo/functionCall/UnmappedAfterCurrent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UnmappedBeforeCurrent.kt")
|
||||
public void testUnmappedBeforeCurrent() throws Exception {
|
||||
runTest("testData/parameterInfo/functionCall/UnmappedBeforeCurrent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UnmappedCurrent.kt")
|
||||
public void testUnmappedCurrent() throws Exception {
|
||||
runTest("testData/parameterInfo/functionCall/UnmappedCurrent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UnresolvedAfterCurrent.kt")
|
||||
public void testUnresolvedAfterCurrent() throws Exception {
|
||||
runTest("testData/parameterInfo/functionCall/UnresolvedAfterCurrent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UnresolvedBeforeCurrent.kt")
|
||||
public void testUnresolvedBeforeCurrent() throws Exception {
|
||||
runTest("testData/parameterInfo/functionCall/UnresolvedBeforeCurrent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UnresolvedCurrent.kt")
|
||||
public void testUnresolvedCurrent() throws Exception {
|
||||
runTest("testData/parameterInfo/functionCall/UnresolvedCurrent.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("UpdateOnTyping.kt")
|
||||
public void testUpdateOnTyping() throws Exception {
|
||||
runTest("testData/parameterInfo/functionCall/UpdateOnTyping.kt");
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_FIR
|
||||
class A {
|
||||
operator fun get(x: Int) {}
|
||||
operator fun set(x: String, y: Int, value: Int) {}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_FIR
|
||||
fun foo() {
|
||||
fun fff(p: String, c: Char) {}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// IGNORE_FIR
|
||||
// TODO: Fails because no argument mapping is available when there are multiple ambiguous candidates
|
||||
open class A(x: Int) {
|
||||
fun m(x: Int, y: Boolean) = 2
|
||||
fun m(x: Int) = 1
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fun m(x: Int, y: Boolean) = 2
|
||||
|
||||
fun d() {
|
||||
m(<caret>y = false, unmapped = false)
|
||||
}
|
||||
/*
|
||||
Text: (<highlight>[y: Boolean]</highlight>, [x: Int]), Disabled: false, Strikeout: false, Green: true
|
||||
*/
|
||||
@@ -0,0 +1,8 @@
|
||||
fun m(x: Int, y: Boolean) = 2
|
||||
|
||||
fun d() {
|
||||
m(unmapped = 1, <caret>y = false)
|
||||
}
|
||||
/*
|
||||
Text: (<highlight>[y: Boolean]</highlight>, [x: Int]), Disabled: true, Strikeout: false, Green: true
|
||||
*/
|
||||
@@ -0,0 +1,8 @@
|
||||
fun m(x: Int, y: Boolean) = 2
|
||||
|
||||
fun d() {
|
||||
m(y = false, <caret>unmapped = false)
|
||||
}
|
||||
/*
|
||||
Text: (<disabled>[y: Boolean],</disabled><highlight> </highlight>[x: Int]), Disabled: false, Strikeout: false, Green: true
|
||||
*/
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fun m(x: Int, y: Boolean) = 2
|
||||
|
||||
fun d() {
|
||||
m(1, <caret>unresolved)
|
||||
}
|
||||
/*
|
||||
Text: (x: Int, <highlight>y: Boolean</highlight>), Disabled: false, Strikeout: false, Green: true
|
||||
*/
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fun m(x: Int, y: Boolean) = 2
|
||||
|
||||
fun d() {
|
||||
m(unresolved, <caret>true)
|
||||
}
|
||||
/*
|
||||
Text: (x: Int, <highlight>y: Boolean</highlight>), Disabled: false, Strikeout: false, Green: true
|
||||
*/
|
||||
@@ -0,0 +1,8 @@
|
||||
fun m(x: Int, y: Boolean) = 2
|
||||
|
||||
fun d() {
|
||||
m(<caret>1, unresolved)
|
||||
}
|
||||
/*
|
||||
Text: (<highlight>x: Int</highlight>, y: Boolean), Disabled: false, Strikeout: false, Green: true
|
||||
*/
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_FIR
|
||||
fun fff(p: String, c: Char) {}
|
||||
|
||||
fun foo() {
|
||||
|
||||
Reference in New Issue
Block a user