[FIR] Resolve array literal with non-primitive-array expected type as arrayOf call

This lets us properly complete the call which fixes some issues with
false-positive type mismatches.
This change doesn't apply to array literals in annotation calls yet
because they are resolved as context-dependent.
This will be adapted in a following commit.

#KT-59581
This commit is contained in:
Kirill Rakhman
2023-07-04 14:27:43 +02:00
committed by Space Team
parent 4ead02271b
commit ff6b3350ae
16 changed files with 92 additions and 118 deletions
@@ -41,7 +41,7 @@ object FirAnnotationExpressionChecker : FirAnnotationCallChecker() {
val argumentMapping = expression.argumentMapping.mapping
val fqName = expression.fqName(context.session)
for (arg in argumentMapping.values) {
val argExpression = (arg as? FirNamedArgumentExpression)?.expression ?: arg
val argExpression = (arg as? FirNamedArgumentExpression)?.expression ?: (arg as? FirErrorExpression)?.expression ?: arg
checkAnnotationArgumentWithSubElements(argExpression, context.session, reporter, context)
?.let { reporter.reportOn(argExpression.source, it, context) }
}
@@ -18,11 +18,7 @@ import org.jetbrains.kotlin.diagnostics.reportOn
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.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirErrorTypeRef
import org.jetbrains.kotlin.fir.types.UnexpandedTypeCheck
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.isArrayType
import org.jetbrains.kotlin.fir.types.*
object FirNamedVarargChecker : FirCallChecker() {
override fun check(expression: FirCall, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -74,7 +70,7 @@ object FirNamedVarargChecker : FirCallChecker() {
if (expression is FirArrayOfCall) {
// FirArrayOfCall has the `vararg` argument expression pre-flattened and doesn't have an argument mapping.
expression.arguments.forEach { checkArgument(it, it is FirNamedArgumentExpression, null /* not used for annotation call */) }
expression.arguments.forEach { checkArgument(it, it is FirNamedArgumentExpression, expression.typeRef.coneTypeOrNull) }
} else {
val argumentMap = expression.resolvedArgumentMapping ?: return
for ((argument, parameter) in argumentMap) {
@@ -54,6 +54,7 @@ object FirUnsupportedArrayLiteralChecker : FirArrayOfCallChecker() {
for (unwrapped in unwrappedArguments) {
if (unwrapped == expression ||
(unwrapped is FirErrorExpression && unwrapped.expression == expression) ||
unwrapped is FirArrayOfCall &&
unwrapped.arguments.any { arrayOfCallElement -> arrayOfCallElement.unwrapArgument() == expression }
) {
@@ -56,6 +56,9 @@ val ConeKotlinType.isNonPrimitiveArray: Boolean
val ConeKotlinType.isPrimitiveArray: Boolean
get() = this is ConeClassLikeType && lookupTag.classId in StandardClassIds.primitiveArrayTypeByElementType.values
val ConeKotlinType.isPrimitiveOrUnsignedArray: Boolean
get() = this is ConeClassLikeType && (lookupTag.classId in StandardClassIds.primitiveArrayTypeByElementType.values || lookupTag.classId in StandardClassIds.unsignedArrayTypeByElementType.values)
val ConeKotlinType.isUnsignedTypeOrNullableUnsignedType: Boolean get() = isAnyOfBuiltinType(StandardClassIds.unsignedTypes)
val ConeKotlinType.isUnsignedType: Boolean get() = isUnsignedTypeOrNullableUnsignedType && nullability == ConeNullability.NOT_NULL
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.transformers
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fakeElement
@@ -22,6 +23,7 @@ import org.jetbrains.kotlin.fir.expressions.builder.buildFunctionCall
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.references.FirResolvedCallableReference
import org.jetbrains.kotlin.fir.references.builder.buildErrorNamedReference
import org.jetbrains.kotlin.fir.references.builder.buildResolvedErrorReference
import org.jetbrains.kotlin.fir.references.impl.FirStubReference
import org.jetbrains.kotlin.fir.references.isError
@@ -30,6 +32,8 @@ import org.jetbrains.kotlin.fir.resolve.ResolutionMode
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.createErrorReferenceWithExistingCandidate
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.toErrorReference
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
import org.jetbrains.kotlin.fir.symbols.SyntheticCallableId
@@ -44,6 +48,7 @@ import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.ArrayFqNames
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.types.Variance
@@ -59,6 +64,12 @@ class FirSyntheticCallGenerator(
private val checkNotNullFunction: FirSimpleFunction = generateSyntheticCheckNotNullFunction()
private val elvisFunction: FirSimpleFunction = generateSyntheticElvisFunction()
private val arrayOfSymbol by lazy(LazyThreadSafetyMode.NONE) {
session.symbolProvider
.getTopLevelFunctionSymbols(StandardNames.BUILT_INS_PACKAGE_FQ_NAME, ArrayFqNames.ARRAY_OF_FUNCTION)
.firstOrNull()
}
private fun assertSyntheticResolvableReferenceIsNotResolved(resolvable: FirResolvable) {
// All synthetic calls (FirWhenExpression, FirTryExpression, FirElvisExpression, FirCheckNotNullCall)
// contains FirStubReference on creation.
@@ -139,7 +150,7 @@ class FirSyntheticCallGenerator(
return elvisExpression.transformCalleeReference(UpdateReference, reference)
}
fun generateSyntheticCallForArrayOfCall(arrayOfCall: FirArrayOfCall, context: ResolutionContext): FirFunctionCall {
fun generateSyntheticIdCall(arrayOfCall: FirExpression, context: ResolutionContext): FirFunctionCall {
val argumentList = buildArgumentList {
arguments += arrayOfCall
}
@@ -155,6 +166,26 @@ class FirSyntheticCallGenerator(
}
}
fun generateSyntheticArrayOfCall(arrayOfCall: FirArrayOfCall, context: ResolutionContext): FirFunctionCall {
val argumentList = arrayOfCall.argumentList
return buildFunctionCall {
this.argumentList = argumentList
calleeReference = arrayOfSymbol?.let {
generateCalleeReferenceWithCandidate(
arrayOfCall,
it.fir,
argumentList,
ArrayFqNames.ARRAY_OF_FUNCTION,
callKind = CallKind.Function,
context = context,
)
} ?: buildErrorNamedReference {
diagnostic = ConeUnresolvedNameError(ArrayFqNames.ARRAY_OF_FUNCTION)
}
source = arrayOfCall.source
}
}
fun resolveCallableReferenceWithSyntheticOuterCall(
callableReferenceAccess: FirCallableReferenceAccess,
expectedTypeRef: FirTypeRef?,
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
@@ -14,6 +16,7 @@ import org.jetbrains.kotlin.fir.expressions.builder.buildArgumentList
import org.jetbrains.kotlin.fir.expressions.builder.buildArrayOfCall
import org.jetbrains.kotlin.fir.references.FirResolvedErrorReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.isError
import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
@@ -28,22 +31,34 @@ import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
* Note that arrayOf() calls only in [FirAnnotation] or the default value of annotation constructor are transformed.
*/
class FirArrayOfCallTransformer : FirDefaultTransformer<FirSession>() {
private fun toArrayOfCall(functionCall: FirFunctionCall, session: FirSession): FirArrayOfCall? {
private fun toArrayOfCall(functionCall: FirFunctionCall, session: FirSession): FirExpression? {
if (!functionCall.isArrayOfCall(session)) return null
if (functionCall.calleeReference !is FirResolvedNamedReference) return null
return buildArrayOfCall {
val arrayOfCall = buildArrayOfCall {
source = functionCall.source
annotations += functionCall.annotations
// Note that the signature is: arrayOf(vararg element). Hence, unwrapping the original argument list here.
argumentList = buildArgumentList {
if (functionCall.arguments.isNotEmpty()) {
(functionCall.argument as FirVarargArgumentsExpression).arguments.forEach {
arguments += it
functionCall.arguments.flatMapTo(arguments) {
if (it is FirVarargArgumentsExpression) it.arguments else listOf(it)
}
}
}
typeRef = functionCall.typeRef
}
val calleeReference = functionCall.calleeReference
return if (calleeReference.isError()) {
buildErrorExpression(
functionCall.source?.fakeElement(KtFakeSourceElementKind.ErrorTypeRef),
calleeReference.diagnostic,
arrayOfCall
)
} else {
arrayOfCall
}
}
override fun transformFunctionCall(functionCall: FirFunctionCall, data: FirSession): FirStatement {
@@ -83,7 +98,7 @@ class FirArrayOfCallTransformer : FirDefaultTransformer<FirSession>() {
private fun FirFunctionCall.getOriginalFunction(): FirCallableDeclaration? {
val symbol: FirBasedSymbol<*>? = when (val reference = calleeReference) {
is FirResolvedErrorReference -> null
is FirResolvedErrorReference -> reference.resolvedSymbol
is FirResolvedNamedReference -> reference.resolvedSymbol
is FirNamedReferenceWithCandidate -> reference.candidateSymbol
else -> null
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.*
@@ -30,6 +31,7 @@ import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.inference.FirStubInferenceSession
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.transformers.replaceLambdaArgumentInvocationKinds
import org.jetbrains.kotlin.fir.scopes.impl.isWrappedIntegerOperator
@@ -45,6 +47,7 @@ import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.ArrayFqNames
import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.ConstantValueKind
@@ -1635,12 +1638,18 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
whileAnalysing(session, arrayOfCall) {
if (data is ResolutionMode.ContextDependent) {
arrayOfCall.transformChildren(transformer, data)
return arrayOfCall
arrayOfCall
} else if (data is ResolutionMode.WithExpectedType && !data.expectedTypeRef.coneType.isPrimitiveOrUnsignedArray) {
arrayOfCall.transformChildren(transformer, ResolutionMode.ContextDependent)
val call = components.syntheticCallGenerator.generateSyntheticArrayOfCall(arrayOfCall, resolutionContext)
callCompleter.completeCall(call, data)
arrayOfCallTransformer.transformFunctionCall(call, session)
} else {
val syntheticIdCall = components.syntheticCallGenerator.generateSyntheticIdCall(arrayOfCall, resolutionContext)
arrayOfCall.transformChildren(transformer, ResolutionMode.ContextDependent)
callCompleter.completeCall(syntheticIdCall, data)
arrayOfCall
}
val syntheticIdCall = components.syntheticCallGenerator.generateSyntheticCallForArrayOfCall(arrayOfCall, resolutionContext)
arrayOfCall.transformChildren(transformer, ResolutionMode.ContextDependent)
callCompleter.completeCall(syntheticIdCall, data)
return arrayOfCall
}
override fun transformStringConcatenationCall(
@@ -19,7 +19,6 @@ annotation class Foo(
val a: IntArray = [],
val b: IntArray = [1, 2, 3],
val c: Array<String> = ["/"],
@Suppress("INITIALIZER_TYPE_MISMATCH") // Remove after KT-59581 is fixed
val d: Array<KClass<*>> = [Int::class, Array<Int>::class],
val e: DoubleArray = [1.0]
)
@@ -6,7 +6,7 @@ fun test() {
val b: Array<Int> = []
val c = [1, 2]
val d: Array<Int> = [1, 2]
val e: Array<String> = <!INITIALIZER_TYPE_MISMATCH!>[1]<!>
val e: Array<String> = <!TYPE_MISMATCH, TYPE_MISMATCH!>[1]<!>
val f: IntArray = [1, 2]
val g = [f]
@@ -1,13 +1,16 @@
import kotlin.reflect.KClass
annotation class Foo(
val a: Array<String> = ["/"],
val b: Array<String> = [],
val c: Array<String> = ["1", "2"]
val c: Array<String> = ["1", "2"],
val d: Array<KClass<*>> = [Int::class, Array<Int>::class],
)
annotation class Bar(
val a: Array<String> = <!INITIALIZER_TYPE_MISMATCH!>[' ']<!>,
val a: Array<String> = <!TYPE_MISMATCH, TYPE_MISMATCH!>[' ']<!>,
val b: Array<String> = ["", <!EMPTY_CHARACTER_LITERAL!>''<!>],
val c: Array<String> = <!INITIALIZER_TYPE_MISMATCH!>[1]<!>
val c: Array<String> = <!TYPE_MISMATCH, TYPE_MISMATCH!>[1]<!>
)
annotation class Base(
@@ -19,5 +22,5 @@ annotation class Base(
annotation class Err(
val a: IntArray = <!INITIALIZER_TYPE_MISMATCH!>[1L]<!>,
val b: Array<String> = <!INITIALIZER_TYPE_MISMATCH!>[1]<!>
val b: Array<String> = <!TYPE_MISMATCH, TYPE_MISMATCH!>[1]<!>
)
@@ -1,7 +1,10 @@
import kotlin.reflect.KClass
annotation class Foo(
val a: Array<String> = ["/"],
val b: Array<String> = [],
val c: Array<String> = ["1", "2"]
val c: Array<String> = ["1", "2"],
val d: Array<KClass<*>> = [Int::class, Array<Int>::class],
)
annotation class Bar(
@@ -31,10 +31,11 @@ public final annotation class Err : kotlin.Annotation {
}
public final annotation class Foo : kotlin.Annotation {
public constructor Foo(/*0*/ a: kotlin.Array<kotlin.String> = ..., /*1*/ b: kotlin.Array<kotlin.String> = ..., /*2*/ c: kotlin.Array<kotlin.String> = ...)
public constructor Foo(/*0*/ a: kotlin.Array<kotlin.String> = ..., /*1*/ b: kotlin.Array<kotlin.String> = ..., /*2*/ c: kotlin.Array<kotlin.String> = ..., /*3*/ d: kotlin.Array<kotlin.reflect.KClass<*>> = ...)
public final val a: kotlin.Array<kotlin.String>
public final val b: kotlin.Array<kotlin.String>
public final val c: kotlin.Array<kotlin.String>
public final val d: kotlin.Array<kotlin.reflect.KClass<*>>
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@@ -1,2 +1,2 @@
annotation class A(val a: IntArray = <!INITIALIZER_TYPE_MISMATCH, TYPE_MISMATCH!>arrayOf(1)<!>)
annotation class A(val a: IntArray = <!TYPE_MISMATCH!>arrayOf(1)<!>)
annotation class B(val a: IntArray = intArrayOf(1))
@@ -1,88 +0,0 @@
// MODULE: m1-common
// FILE: common.kt
import kotlin.reflect.KClass
expect annotation class Primitives(
val z: Boolean = true,
val c: Char = 'c',
val b: Byte = 42.toByte(),
val s: Short = (-1).toShort(),
val i: Int = -42,
val f: Float = 2.72f,
val j: Long = 123456789123456789L,
val d: Double = 3.14159265358979
)
expect annotation class PrimitiveArrays(
val z: BooleanArray = [true],
val c: CharArray = ['c'],
val b: ByteArray = [42.toByte()],
val s: ShortArray = [(-1).toShort()],
val i: IntArray = [-42],
val f: FloatArray = [2.72f],
val j: LongArray = [123456789123456789L],
val d: DoubleArray = [3.14159265358979]
)
enum class En { A, B }
annotation class Anno(val value: String = "Anno")
expect annotation class Classes(
val s: String = "OK",
val e: En = En.B,
// TODO: this does not work at the moment because AnnotationDescriptor subclasses do not implement equals correctly
// val a: Anno = Anno(),
val k: KClass<*> = List::class
)
expect annotation class ClassArrays(
val s: Array<String> = ["OK"],
val e: Array<En> = [En.B],
// val a: Array<Anno> = [Anno()],
val k: Array<KClass<*>> = <!INITIALIZER_TYPE_MISMATCH!>[List::class]<!>, // KT-59581
vararg val v: Int = [42]
)
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
import kotlin.reflect.KClass
actual annotation class Primitives(
actual val z: Boolean = true,
actual val c: Char = 'c',
actual val b: Byte = 42.toByte(),
actual val s: Short = (-1).toShort(),
actual val i: Int = -42,
actual val f: Float = 2.72f,
actual val j: Long = 123456789123456789L,
actual val d: Double = 3.14159265358979
)
actual annotation class PrimitiveArrays(
actual val z: BooleanArray = [true],
actual val c: CharArray = ['c'],
actual val b: ByteArray = [42.toByte()],
actual val s: ShortArray = [(-1).toShort()],
actual val i: IntArray = [-42],
actual val f: FloatArray = [2.72f],
actual val j: LongArray = [123456789123456789L],
actual val d: DoubleArray = [3.14159265358979]
)
actual annotation class Classes(
actual val s: String = "OK",
actual val e: En = En.B,
// actual val a: Anno = Anno(),
actual val k: KClass<*> = List::class
)
actual annotation class ClassArrays(
actual val s: Array<String> = ["OK"],
actual val e: Array<En> = [En.B],
// actual val a: Array<Anno> = [Anno()],
actual val k: Array<KClass<*>> = <!INITIALIZER_TYPE_MISMATCH!>[List::class]<!>,
actual vararg val v: Int = [42]
)
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// MODULE: m1-common
// FILE: common.kt
@@ -41,7 +42,7 @@ expect annotation class ClassArrays(
val s: Array<String> = ["OK"],
val e: Array<En> = [En.B],
// val a: Array<Anno> = [Anno()],
val k: Array<KClass<*>> = [List::class], // KT-59581
val k: Array<KClass<*>> = [List::class],
vararg val v: Int = [42]
)
@@ -3,5 +3,5 @@
fun foo(l: () -> Unit) {}
fun bar(l: () -> String) {}
val a = foo { <!UNSUPPORTED!>[]<!> }
val b = bar { <!ARGUMENT_TYPE_MISMATCH, UNSUPPORTED!>[]<!> }
val a = foo { <!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, UNSUPPORTED!>[]<!> }
val b = bar { <!TYPE_MISMATCH, UNSUPPORTED!>[]<!> }