From 2be9a341ca1cac2d0e5485d5bdd89c3982626e9e Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Fri, 12 Jan 2024 22:29:45 +0100 Subject: [PATCH] [FIR] Report `ARGUMENT_TYPE_MISMATCH` for nested array literals ^KT-61843 Fixed --- .../jetbrains/kotlin/fir/FirCallResolver.kt | 55 ++++++++++++------- .../kotlin/fir/resolve/calls/Arguments.kt | 2 +- ...boutChangingExecutionOrderForVararg.fir.kt | 5 +- ...ingAboutChangingExecutionOrderForVararg.kt | 3 + ...ngAboutChangingExecutionOrderForVararg.txt | 1 + 5 files changed, 44 insertions(+), 22 deletions(-) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt index 3fb806689f5..697366ade99 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt @@ -573,33 +573,48 @@ class FirCallResolver( // We want to "desugar" array literal arguments to arrayOf, intArrayOf, floatArrayOf and other *arrayOf* calls // so that we can properly complete them eventually. // In order to find out what the expected type is, we need to run argument mapping. + // Array literals can be nested despite the fact they are not supported in annotation arguments. + // But we should traverse them all recursively to report type mismatches. + // For nested array literal, we need a new expected type obtained from the previous expected type (extract type of array element). // We don't want to force full completion before the whole call is completed so that type variables are preserved. // But we need to pass expectType to figure out the correct *arrayOf* function (because Array and primitive arrays can't be matched). val mapping = transformer.resolutionContext.bodyResolveComponents.mapArguments( annotation.arguments, constructorSymbol.fir, originScope = null, callSiteIsOperatorCall = false, ) val argumentsToParameters = mapping.toArgumentToParameterMapping() - annotation.replaceArgumentList(buildArgumentList { - source = annotation.argumentList.source - annotation.arguments.mapTo(arguments) { arg -> - val resolutionMode = if (arg.unwrapArgument() is FirArrayLiteral) { - (argumentsToParameters[arg]?.returnTypeRef as? FirResolvedTypeRef)?.let { - // Enabling expectedTypeMismatchIsReportedInChecker clarifies error messages: - // It will be reported single ARGUMENT_TYPE_MISMATCH on the array literal in checkApplicabilityForArgumentType - // instead of several TYPE_MISMATCH for every mismatched argument. - ResolutionMode.WithExpectedType( - it, - forceFullCompletion = false, - expectedTypeMismatchIsReportedInChecker = true - ) - } ?: ResolutionMode.ContextDependent.Default - } else { - ResolutionMode.ContextDependent - } - arg.transformSingle(transformer, resolutionMode) - } - }) + fun FirCall.transformArgumentList(getExpectedType: (FirExpression) -> FirTypeRef?) { + replaceArgumentList( + buildArgumentList { + source = argumentList.source + argumentList.arguments.mapTo(arguments) { arg -> + val unwrappedArgument = arg.unwrapArgument() + val expectedType = getExpectedType(arg) + val resolutionMode = if (unwrappedArgument is FirArrayLiteral && expectedType is FirResolvedTypeRef) { + unwrappedArgument.transformArgumentList { + // Trying to extract expected type for the next nested array literal + expectedType.coneType.arrayElementType()?.toFirResolvedTypeRef() + } + + // Enabling expectedTypeMismatchIsReportedInChecker clarifies error messages: + // It will be reported single ARGUMENT_TYPE_MISMATCH on the array literal in checkApplicabilityForArgumentType + // instead of several TYPE_MISMATCH for every mismatched argument. + ResolutionMode.WithExpectedType( + expectedType, + forceFullCompletion = false, + expectedTypeMismatchIsReportedInChecker = true + ) + } else { + ResolutionMode.ContextDependent + } + + return@mapTo arg.transformSingle(transformer, resolutionMode) + } + } + ) + } + + annotation.transformArgumentList { argumentsToParameters[it]?.returnTypeRef } } else { annotation.replaceArgumentList(annotation.argumentList.transform(transformer, ResolutionMode.ContextDependent.Default)) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index 8135ac139e3..ad717bcfddf 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -238,7 +238,7 @@ fun Candidate.resolvePlainExpressionArgument( if (expectedType == null) return - // TODO Remove when KT-61843 is fixed + // TODO: this check should be eliminated, KT-65085 if (argument is FirArrayLiteral && !argument.isResolved) return val argumentType = argument.resolvedType diff --git a/compiler/testData/diagnostics/tests/annotations/dontReportWarningAboutChangingExecutionOrderForVararg.fir.kt b/compiler/testData/diagnostics/tests/annotations/dontReportWarningAboutChangingExecutionOrderForVararg.fir.kt index a8b58fb1cef..ef953ec540d 100644 --- a/compiler/testData/diagnostics/tests/annotations/dontReportWarningAboutChangingExecutionOrderForVararg.fir.kt +++ b/compiler/testData/diagnostics/tests/annotations/dontReportWarningAboutChangingExecutionOrderForVararg.fir.kt @@ -1,9 +1,12 @@ @Retention(AnnotationRetention.RUNTIME) annotation class Anno(vararg val x: String, val y: String) -@Anno(x = [["a", "b"], ["a", "b"]], y = "a") +@Anno(x = [["a", "b"], ["a", "b"]], y = "a") fun foo1() {} +@Anno(x = [[["a"]]], y = "b") +fun foo11() {} + @Anno(x = ["a", "b"], y = "a") fun foo2() {} diff --git a/compiler/testData/diagnostics/tests/annotations/dontReportWarningAboutChangingExecutionOrderForVararg.kt b/compiler/testData/diagnostics/tests/annotations/dontReportWarningAboutChangingExecutionOrderForVararg.kt index 1e1e6c82b07..7c8f4e0325b 100644 --- a/compiler/testData/diagnostics/tests/annotations/dontReportWarningAboutChangingExecutionOrderForVararg.kt +++ b/compiler/testData/diagnostics/tests/annotations/dontReportWarningAboutChangingExecutionOrderForVararg.kt @@ -4,6 +4,9 @@ annotation class Anno(vararg val x: String, val y: String) @Anno(x = [["a", "b"], ["a", "b"]], y = "a") fun foo1() {} +@Anno(x = [[["a"]]], y = "b") +fun foo11() {} + @Anno(x = ["a", "b"], y = "a") fun foo2() {} diff --git a/compiler/testData/diagnostics/tests/annotations/dontReportWarningAboutChangingExecutionOrderForVararg.txt b/compiler/testData/diagnostics/tests/annotations/dontReportWarningAboutChangingExecutionOrderForVararg.txt index fb9586c3c83..ed87408ffe4 100644 --- a/compiler/testData/diagnostics/tests/annotations/dontReportWarningAboutChangingExecutionOrderForVararg.txt +++ b/compiler/testData/diagnostics/tests/annotations/dontReportWarningAboutChangingExecutionOrderForVararg.txt @@ -1,6 +1,7 @@ package @Anno(x = {{"a", "b"}, {"a", "b"}}, y = "a") public fun foo1(): kotlin.Unit +@Anno(x = {{{"a"}}}, y = "b") public fun foo11(): kotlin.Unit @Anno(x = {"a", "b"}, y = "a") public fun foo2(): kotlin.Unit @Anno(x = {{"a"}, {"b"}}, y = "a") public fun foo3(): kotlin.Unit @Anno(x = {"a", "b"}, y = "a") public fun foo4(): kotlin.Unit