[FIR] Remove FirNamedArgumentExpressions during completion
They are mostly necessary for argument mapping during resolution. To support a couple checkers, we transform named args for varargs into "fake" spread expressions. Other than that, named arguments aren't needed for anything and often lead to bugs where we forget to unwrap them for something, so it's better to get rid of them. #KT-66124
This commit is contained in:
committed by
Space Team
parent
03fc0fd381
commit
8443daf78d
@@ -5,15 +5,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.calls
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirCallResolver
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.expressions.FirArgumentList
|
||||
import org.jetbrains.kotlin.fir.expressions.FirEmptyArgumentList
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCallOrigin
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildArgumentList
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
|
||||
import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS
|
||||
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
@@ -47,8 +46,18 @@ data class CallInfo(
|
||||
val hasSyntheticOuterCall: Boolean = false,
|
||||
val origin: FirFunctionCallOrigin = FirFunctionCallOrigin.Regular,
|
||||
) : AbstractCallInfo() {
|
||||
val arguments: List<FirExpression> get() = argumentList.arguments
|
||||
|
||||
/**
|
||||
* If [argumentList] is a [FirResolvedArgumentList],
|
||||
* returns the [FirArgumentList.arguments] of the [FirResolvedArgumentList.originalArgumentList].
|
||||
* This means the result still contains [FirNamedArgumentExpression]s that are removed from the
|
||||
* [FirResolvedArgumentList] during completion.
|
||||
*
|
||||
* This is important for Analysis API because it will trigger resolution on already resolved expressions,
|
||||
* and we wouldn't otherwise have access to named arguments.
|
||||
*
|
||||
* @see FirCallResolver.collectAllCandidates
|
||||
*/
|
||||
val arguments: List<FirExpression> get() = (argumentList as? FirResolvedArgumentList)?.originalArgumentList?.arguments ?: argumentList.arguments
|
||||
val argumentCount get() = arguments.size
|
||||
|
||||
fun replaceWithVariableAccess(): CallInfo =
|
||||
|
||||
+36
-10
@@ -17,6 +17,8 @@ import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildSamConversionExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildSpreadArgumentExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
|
||||
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedErrorReference
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildResolvedCallableReference
|
||||
@@ -345,16 +347,17 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
val calleeReference = functionCall.calleeReference as? FirNamedReferenceWithCandidate
|
||||
?: return functionCall
|
||||
val result = prepareQualifiedTransform(functionCall, calleeReference)
|
||||
val originalArgumentList = result.argumentList
|
||||
val subCandidate = calleeReference.candidate
|
||||
val resultType = result.resolvedType.substituteType(subCandidate)
|
||||
if (calleeReference.isError) {
|
||||
subCandidate.argumentMapping?.let {
|
||||
result.replaceArgumentList(buildArgumentListForErrorCall(result.argumentList, it))
|
||||
result.replaceArgumentList(buildArgumentListForErrorCall(originalArgumentList, it))
|
||||
}
|
||||
} else {
|
||||
subCandidate.handleVarargs()
|
||||
subCandidate.argumentMapping?.let {
|
||||
val newArgumentList = buildResolvedArgumentList(it, source = functionCall.argumentList.source)
|
||||
val newArgumentList = buildResolvedArgumentList(originalArgumentList, it)
|
||||
val symbol = subCandidate.symbol
|
||||
val functionIsInline =
|
||||
(symbol as? FirNamedFunctionSymbol)?.fir?.isInline == true || symbol.isArrayConstructorWithLambda
|
||||
@@ -375,7 +378,7 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
}
|
||||
val expectedArgumentsTypeMapping = runIf(!calleeReference.isError) { subCandidate.createArgumentsMapping() }
|
||||
|
||||
result.transformWithExpectedTypes(expectedArgumentsTypeMapping)
|
||||
result.transformArgumentList(expectedArgumentsTypeMapping)
|
||||
|
||||
result.replaceConeTypeOrNull(resultType)
|
||||
session.lookupTracker?.recordTypeResolveAsLookup(resultType, functionCall.source, context.file.source)
|
||||
@@ -398,8 +401,10 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirCall.transformWithExpectedTypes(expectedArgumentsTypeMapping: ExpectedArgumentType.ArgumentsMap?) {
|
||||
class SamConversionInsertionTransformer : FirTransformer<Nothing?>() {
|
||||
private fun FirCall.transformArgumentList(expectedArgumentsTypeMapping: ExpectedArgumentType.ArgumentsMap?) {
|
||||
val mapping = (argumentList as? FirResolvedArgumentList)?.mapping
|
||||
|
||||
class ArgumentTransformer : FirTransformer<Nothing?>() {
|
||||
override fun <E : FirElement> transformElement(element: E, data: Nothing?): E {
|
||||
// We want to handle only the most top-level "real" expressions
|
||||
// We only recursively transform named, spread, lambda argument and vararg expressions.
|
||||
@@ -422,9 +427,27 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
|
||||
return transformed
|
||||
}
|
||||
|
||||
override fun transformNamedArgumentExpression(
|
||||
namedArgumentExpression: FirNamedArgumentExpression,
|
||||
data: Nothing?
|
||||
): FirStatement {
|
||||
val expression = transformElement(namedArgumentExpression.expression, data)
|
||||
val parameter = mapping?.get(namedArgumentExpression)
|
||||
return if (namedArgumentExpression.isSpread || parameter?.isVararg == true) {
|
||||
buildSpreadArgumentExpression {
|
||||
this.source = namedArgumentExpression.source
|
||||
this.expression = expression
|
||||
this.isNamed = true
|
||||
this.isFakeSpread = !namedArgumentExpression.isSpread
|
||||
}
|
||||
} else {
|
||||
expression
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
argumentList.transformArguments(SamConversionInsertionTransformer(), null)
|
||||
argumentList.transformArguments(ArgumentTransformer(), null)
|
||||
}
|
||||
|
||||
private fun FirExpression.wrapInSamExpression(expectedArgumentType: ConeKotlinType): FirExpression {
|
||||
@@ -468,9 +491,11 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
} else {
|
||||
subCandidate.handleVarargs()
|
||||
subCandidate.argumentMapping?.let {
|
||||
annotationCall.replaceArgumentList(buildResolvedArgumentList(it, annotationCall.argumentList.source))
|
||||
annotationCall.replaceArgumentList(buildResolvedArgumentList(annotationCall.argumentList, it))
|
||||
}
|
||||
}
|
||||
|
||||
annotationCall.transformArgumentList(expectedArgumentsTypeMapping = null)
|
||||
return annotationCall
|
||||
}
|
||||
|
||||
@@ -673,21 +698,22 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
delegatedConstructorCall.calleeReference as? FirNamedReferenceWithCandidate ?: return delegatedConstructorCall
|
||||
val subCandidate = calleeReference.candidate
|
||||
|
||||
val originalArgumentList = delegatedConstructorCall.argumentList
|
||||
if (calleeReference.isError) {
|
||||
subCandidate.argumentMapping?.let {
|
||||
delegatedConstructorCall.replaceArgumentList(buildArgumentListForErrorCall(delegatedConstructorCall.argumentList, it))
|
||||
delegatedConstructorCall.replaceArgumentList(buildArgumentListForErrorCall(originalArgumentList, it))
|
||||
}
|
||||
} else {
|
||||
subCandidate.handleVarargs()
|
||||
subCandidate.argumentMapping?.let {
|
||||
delegatedConstructorCall.replaceArgumentList(buildResolvedArgumentList(it, delegatedConstructorCall.argumentList.source))
|
||||
delegatedConstructorCall.replaceArgumentList(buildResolvedArgumentList(originalArgumentList, it))
|
||||
}
|
||||
}
|
||||
|
||||
runPCLARelatedTasksForCandidate(subCandidate)
|
||||
|
||||
val argumentsMapping = runIf(!calleeReference.isError) { subCandidate.createArgumentsMapping() }
|
||||
delegatedConstructorCall.transformWithExpectedTypes(argumentsMapping)
|
||||
delegatedConstructorCall.transformArgumentList(argumentsMapping)
|
||||
|
||||
return delegatedConstructorCall.apply {
|
||||
replaceCalleeReference(calleeReference.toResolvedReference())
|
||||
|
||||
+11
-1
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.expressions.FirBlock
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.UnresolvedExpressionTypeAccess
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildSpreadArgumentExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildVarargArgumentsExpression
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
@@ -57,7 +58,16 @@ internal fun remapArgumentsWithVararg(
|
||||
// NB: don't pull out of named arguments.
|
||||
(valueParameter.isVararg && arg !is FirNamedArgumentExpression)
|
||||
) {
|
||||
arguments += arg
|
||||
arguments += if (arg is FirNamedArgumentExpression) {
|
||||
buildSpreadArgumentExpression {
|
||||
this.source = arg.source
|
||||
this.expression = arg.expression
|
||||
this.isNamed = true
|
||||
this.isFakeSpread = !arg.isSpread
|
||||
}
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
startOffset = minOf(startOffset, arg.source?.startOffset ?: Int.MAX_VALUE)
|
||||
endOffset = maxOf(endOffset, arg.source?.endOffset ?: 0)
|
||||
if (firstVarargElementSource == null) firstVarargElementSource = arg.source
|
||||
|
||||
Reference in New Issue
Block a user