[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
+3
-2
@@ -48,8 +48,9 @@ object FirJavaAnnotationsChecker : FirAnnotationChecker(MppCheckerKind.Common) {
|
||||
if (expression is FirAnnotationCall) {
|
||||
val argumentList = expression.argumentList
|
||||
if (argumentList is FirResolvedArgumentList) {
|
||||
for ((key, value) in argumentList.mapping) {
|
||||
if (value.name != Annotations.ParameterNames.value && key !is FirWrappedArgumentExpression) {
|
||||
val arguments = argumentList.originalArgumentList?.arguments ?: return
|
||||
for (key in arguments) {
|
||||
if (key !is FirWrappedArgumentExpression && argumentList.mapping[key]?.name.let { it != null && it != Annotations.ParameterNames.value}) {
|
||||
reporter.reportOn(key.source, FirJvmErrors.POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,10 @@ private class FirConstCheckVisitor(private val session: FirSession) : FirVisitor
|
||||
return namedArgumentExpression.expression.accept(this, data)
|
||||
}
|
||||
|
||||
override fun visitSpreadArgumentExpression(spreadArgumentExpression: FirSpreadArgumentExpression, data: Nothing?): ConstantArgumentKind {
|
||||
return spreadArgumentExpression.expression.accept(this, data)
|
||||
}
|
||||
|
||||
override fun visitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: Nothing?): ConstantArgumentKind {
|
||||
return if (typeOperatorCall.operation == FirOperation.AS) ConstantArgumentKind.NOT_CONST else ConstantArgumentKind.VALID_CONST
|
||||
}
|
||||
|
||||
+2
-3
@@ -50,7 +50,7 @@ object FirAnnotationExpressionChecker : FirAnnotationCallChecker(MppCheckerKind.
|
||||
val annotationClassId = expression.toAnnotationClassId(context.session)
|
||||
val fqName = annotationClassId?.asSingleFqName()
|
||||
for (arg in argumentMapping.values) {
|
||||
val argExpression = (arg as? FirNamedArgumentExpression)?.expression ?: (arg as? FirErrorExpression)?.expression ?: arg
|
||||
val argExpression = (arg as? FirErrorExpression)?.expression ?: arg
|
||||
checkAnnotationArgumentWithSubElements(argExpression, context.session, reporter, context)
|
||||
?.let { reporter.reportOn(argExpression.source, it, context) }
|
||||
}
|
||||
@@ -123,8 +123,7 @@ object FirAnnotationExpressionChecker : FirAnnotationCallChecker(MppCheckerKind.
|
||||
context: CheckerContext,
|
||||
reporter: DiagnosticReporter
|
||||
): ApiVersion? {
|
||||
val constantExpression = (expression as? FirLiteralExpression<*>)
|
||||
?: ((expression as? FirNamedArgumentExpression)?.expression as? FirLiteralExpression<*>) ?: return null
|
||||
val constantExpression = (expression as? FirLiteralExpression<*>) ?: return null
|
||||
val stringValue = constantExpression.value as? String ?: return null
|
||||
if (!stringValue.matches(RequireKotlinConstants.VERSION_REGEX)) {
|
||||
reporter.reportOn(expression.source, FirErrors.ILLEGAL_KOTLIN_VERSION_STRING_VALUE, context)
|
||||
|
||||
+15
-5
@@ -20,6 +20,8 @@ import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
object FirNamedVarargChecker : FirCallChecker(MppCheckerKind.Common) {
|
||||
override fun check(expression: FirCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
@@ -38,8 +40,8 @@ object FirNamedVarargChecker : FirCallChecker(MppCheckerKind.Common) {
|
||||
)
|
||||
|
||||
fun checkArgument(argument: FirExpression, isVararg: Boolean, expectedArrayType: ConeKotlinType) {
|
||||
if (argument !is FirNamedArgumentExpression) return
|
||||
if (argument.isSpread) {
|
||||
if (!isNamedSpread(argument)) return
|
||||
if (!argument.isFakeSpread && argument.isNamed) {
|
||||
if (isVararg && (expression as? FirResolvable)?.calleeReference !is FirErrorNamedReference) {
|
||||
reporter.reportOn(argument.expression.source, redundantSpreadWarningFactory, context)
|
||||
}
|
||||
@@ -69,17 +71,25 @@ object FirNamedVarargChecker : FirCallChecker(MppCheckerKind.Common) {
|
||||
|
||||
if (expression is FirArrayLiteral) {
|
||||
// FirArrayLiteral has the `vararg` argument expression pre-flattened and doesn't have an argument mapping.
|
||||
expression.arguments.forEach { checkArgument(it, it is FirNamedArgumentExpression, expression.resolvedType) }
|
||||
expression.arguments.forEach { checkArgument(it, isVararg = isNamedSpread(it), expression.resolvedType) }
|
||||
} else {
|
||||
val argumentMap = expression.resolvedArgumentMapping ?: return
|
||||
for ((argument, parameter) in argumentMap) {
|
||||
if (!parameter.isVararg) continue
|
||||
if (argument is FirVarargArgumentsExpression) {
|
||||
argument.arguments.forEach { checkArgument(it, true, parameter.returnTypeRef.coneType) }
|
||||
argument.arguments.forEach { checkArgument(it, isVararg = true, parameter.returnTypeRef.coneType) }
|
||||
} else {
|
||||
checkArgument(argument, false, parameter.returnTypeRef.coneType)
|
||||
checkArgument(argument, isVararg = false, parameter.returnTypeRef.coneType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
private fun isNamedSpread(expression: FirExpression): Boolean {
|
||||
contract {
|
||||
returns(true) implies (expression is FirSpreadArgumentExpression)
|
||||
}
|
||||
return expression is FirSpreadArgumentExpression && expression.isNamed
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ object FirSpreadOfNullableChecker : FirFunctionCallChecker(MppCheckerKind.Common
|
||||
override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
fun checkAndReport(argument: FirExpression, source: KtSourceElement?) {
|
||||
val coneType = argument.resolvedType
|
||||
if (argument is FirSpreadArgumentExpression && coneType !is ConeFlexibleType && coneType.canBeNull(context.session)) {
|
||||
if (argument is FirSpreadArgumentExpression && !argument.isFakeSpread && coneType !is ConeFlexibleType && coneType.canBeNull(context.session)) {
|
||||
reporter.reportOn(source, FirErrors.SPREAD_OF_NULLABLE, context)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user