[FIR] KT-54980 Fix resolvability of too few/too many type arguments
- If too few or too many type arguments were provided, they were all
thrown away in `TypeArgumentMapping`,
`FirCallCompletionResultsWriterTransformer`, and `KtFirCallResolver`.
The fix handles type arguments of the wrong arity more gracefully.
- Note for `TypeArgumentMapping`: Excess type arguments are not needed
for candidate resolution. Excess type arguments are still resolved
due to the handling in `FirCallCompletionResultsWriterTransformer`.
- Post-processing in `AllCandidatesResolver`: When all candidates are
resolved in `AllCandidatesResolver.getAllCandidates`, the function
builds a FIR file. During that resolution, the
`generic<String, String>` call (in example
`functionCallWithTooFewTypeArguments.kt`) is correctly marked as
inapplicable, but the missing type argument is inferred as an error
type. `firFile` then contains a function call
`generic<String, String, ERROR>` instead of `generic<String, String>`.
This call is still marked as inapplicable. Despite that, the
*subsequent* resolution by
`bodyResolveComponents.callResolve.collectAllCandidates` disregards
the call's inapplicability and resolves successfully into an
applicable candidate. This is because `CandidateFactory` doesn't make
any guarantees for already inapplicable calls. The fix adds
post-processing to `AllCandidatesResolver` to preserve candidate
inapplicability.
- Most tests that this commit changes had slightly different results due
to type arguments becoming resolvable.
- `wrongNumberOfTypeArguments.kt` and
`wrongNumberOfArgumentsInTypeAliasConstructor.kt`:
`ConeDiagnostic.toFirDiagnostics` prefers specific errors. Because
`ARGUMENT_TYPE_MISMATCH` is specific and `INAPPLICABLE_CANDIDATE` is
not, only the former is reported. I see no reason to pass an illegally
typed argument in either test, so the change reduces the errors to
`INAPPLICABLE_CANDIDATE`.
- `typeAliasSamAdapterConstructors2.fir.kt`: See KT-55007.
- Disable `mismatchTypeParameters` JS backend test due to its handling
of excess type arguments. See KT-55250.
^KT-54980 fixed
This commit is contained in:
committed by
teamcity
parent
d6a9235d05
commit
5f554d0065
+2
-2
@@ -24,9 +24,9 @@ FILE: typeArgumentsNotAllowed.kt
|
||||
}
|
||||
|
||||
}
|
||||
public final val a: <ERROR TYPE REF: Unresolved name: MyClass> = Q|rest|.<Unresolved name: MyClass>#
|
||||
public final val a: <ERROR TYPE REF: Unresolved name: MyClass> = Q|rest|.<Unresolved name: MyClass>#<R|kotlin/String|>
|
||||
public get(): <ERROR TYPE REF: Unresolved name: MyClass>
|
||||
public final val b: <ERROR TYPE REF: Unresolved name: MyClass> = Q|rest/Best|.<Unresolved name: MyClass>#
|
||||
public final val b: <ERROR TYPE REF: Unresolved name: MyClass> = Q|rest/Best|.<Unresolved name: MyClass>#<R|kotlin/String|>
|
||||
public get(): <ERROR TYPE REF: Unresolved name: MyClass>
|
||||
public final class B<E> : R|kotlin/Any| {
|
||||
public constructor<E>(): R|rest/B<E>| {
|
||||
|
||||
+5
-5
@@ -58,21 +58,21 @@ FILE: propertyAccessWithExplicitTypeArguments.kt
|
||||
R|<local>/receiver|.R|/hello1|<R|kotlin/Int|>
|
||||
R|<local>/receiver|.R|/hello1|<R|kotlin/Int|>.R|/invoke|<R|kotlin/Int|, R|kotlin/String|>()
|
||||
R|<local>/receiver|.R|/hello1|<R|kotlin/Int|>
|
||||
R|<local>/receiver|.R|/hello1|<R|kotlin/Int|>
|
||||
R|<local>/receiver|.R|/hello1|<R|kotlin/Int|>
|
||||
R|<local>/receiver|.R|/hello1|<R|kotlin/Int|, R|kotlin/String|>
|
||||
R|<local>/receiver|.R|/hello1|<R|kotlin/Int|, R|kotlin/String|, R|kotlin/String|>
|
||||
R|kotlin/with|<R|ContextImpl<kotlin/String>|, R|kotlin/String|>(R|/ContextImpl.ContextImpl|<R|kotlin/String|>(), <L> = with@fun R|ContextImpl<kotlin/String>|.<anonymous>(): R|kotlin/String| <inline=Inline, kind=EXACTLY_ONCE> {
|
||||
R|/hello2|<R|kotlin/String|>
|
||||
R|/hello2|<R|kotlin/String|>
|
||||
R|/hello2|<R|kotlin/String|>.R|/invoke|<R|kotlin/String|, R|kotlin/Int|>()
|
||||
R|/hello2|<R|kotlin/String|>
|
||||
R|/hello2|<R|kotlin/String|>
|
||||
R|/hello2|<R|kotlin/String|>
|
||||
R|/hello2|<R|kotlin/String|, R|kotlin/Int|>
|
||||
R|/hello2|<R|kotlin/String|, R|kotlin/Int|, R|kotlin/Int|>
|
||||
R|<local>/receiver|.R|/hello3|<R|kotlin/Int|, R|kotlin/String|>
|
||||
R|<local>/receiver|.R|/hello3|<R|kotlin/Int|, R|kotlin/String|>
|
||||
R|<local>/receiver|.R|/hello3|<R|kotlin/Int|, R|kotlin/String|>.R|/invoke|<R|kotlin/Int|, R|kotlin/String|>()
|
||||
R|<local>/receiver|.R|/hello3|<R|kotlin/Int|, R|kotlin/String|>
|
||||
R|<local>/receiver|.R|/hello3|<R|kotlin/Int|, R|kotlin/String|>
|
||||
^ R|<local>/receiver|.R|/hello3|<R|kotlin/Int|, R|kotlin/String|>
|
||||
^ R|<local>/receiver|.R|/hello3|<R|kotlin/Int|, R|kotlin/String|, R|kotlin/String|>
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
+3
-2
@@ -14,8 +14,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors
|
||||
import org.jetbrains.kotlin.fir.declarations.isJavaOrEnhancement
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.resolvedArgumentMapping
|
||||
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.originalOrSelf
|
||||
import org.jetbrains.kotlin.fir.references.toResolvedCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
|
||||
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
|
||||
import org.jetbrains.kotlin.types.model.typeConstructor
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* Checks compatibility of variance of type argument for Java collections.
|
||||
@@ -53,7 +54,7 @@ object FirJavaGenericVarianceViolationTypeChecker : FirFunctionCallChecker() {
|
||||
}
|
||||
val argumentMapping = expression.resolvedArgumentMapping ?: return
|
||||
val typeArgumentMap = mutableMapOf<FirTypeParameterSymbol, ConeKotlinType>()
|
||||
for (i in 0 until expression.typeArguments.size) {
|
||||
for (i in 0 until min(expression.typeArguments.size, calleeFunction.typeParameterSymbols.size)) {
|
||||
val type = (expression.typeArguments[i] as? FirTypeProjectionWithVariance)?.typeRef?.coneType
|
||||
if (type != null) {
|
||||
typeArgumentMap[calleeFunction.typeParameterSymbols[i]] = type
|
||||
|
||||
@@ -43,6 +43,10 @@ class CandidateFactory private constructor(
|
||||
|
||||
constructor(context: ResolutionContext, callInfo: CallInfo) : this(context, buildBaseSystem(context, callInfo))
|
||||
|
||||
/**
|
||||
* [createCandidate] doesn't make any guarantees for inapplicable calls. Errors in the call or callee do not necessarily result in an
|
||||
* inapplicable [Candidate].
|
||||
*/
|
||||
fun createCandidate(
|
||||
callInfo: CallInfo,
|
||||
symbol: FirBasedSymbol<*>,
|
||||
|
||||
+4
-1
@@ -52,7 +52,10 @@ internal object MapTypeArguments : ResolutionStage() {
|
||||
) {
|
||||
candidate.typeArgumentMapping = TypeArgumentMapping.Mapped(typeArguments)
|
||||
} else {
|
||||
candidate.typeArgumentMapping = TypeArgumentMapping.Mapped(emptyList())
|
||||
candidate.typeArgumentMapping = TypeArgumentMapping.Mapped(
|
||||
if (typeArguments.size > owner.typeParameters.size) typeArguments.take(owner.typeParameters.size)
|
||||
else typeArguments
|
||||
)
|
||||
sink.yieldDiagnostic(InapplicableCandidate)
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -513,7 +513,7 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
access: FirQualifiedAccess,
|
||||
candidate: Candidate
|
||||
): List<FirTypeProjection> {
|
||||
return computeTypeArgumentTypes(candidate)
|
||||
val typeArguments = computeTypeArgumentTypes(candidate)
|
||||
.mapIndexed { index, type ->
|
||||
when (val argument = access.typeArguments.getOrNull(index)) {
|
||||
is FirTypeProjectionWithVariance -> {
|
||||
@@ -540,6 +540,12 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We must ensure that all extra type arguments are preserved in the result, so that they can still be resolved later (e.g. for
|
||||
// navigation in the IDE).
|
||||
return if (typeArguments.size < access.typeArguments.size) {
|
||||
typeArguments + access.typeArguments.subList(typeArguments.size, access.typeArguments.size)
|
||||
} else typeArguments
|
||||
}
|
||||
|
||||
private fun computeTypeArgumentTypes(
|
||||
|
||||
Reference in New Issue
Block a user