[FE, IR] Check compatibility of annotations set on type usages in expect and actual declarations

This includes checking of annotatins set on:

- value parameter types
- type parameter bound types
- extension functions receiver types
- function return types
- class super types

Fix in `defaultParams_inheritanceByDelegation_positive.kt`
is needed because of problem in resolution of implicit return types
(KT-62064), which leads to crash in annotation checker, because it
expects resolved return type.

MR: KT-MR-12245

^KT-60671 Fixed
This commit is contained in:
Roman Efremov
2023-09-11 13:24:29 +02:00
committed by Space Team
parent cb8529d65b
commit 423f4ca5f0
22 changed files with 314 additions and 41 deletions
@@ -932,6 +932,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/typeUsage.kt");
}
@Test
@TestMetadata("typeUsageTypealiasInSuper.kt")
public void testTypeUsageTypealiasInSuper() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/typeUsageTypealiasInSuper.kt");
}
@Test
@TestMetadata("typeUsageWithImplicitType.kt")
public void testTypeUsageWithImplicitType() throws Exception {
@@ -932,6 +932,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/typeUsage.kt");
}
@Test
@TestMetadata("typeUsageTypealiasInSuper.kt")
public void testTypeUsageTypealiasInSuper() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/typeUsageTypealiasInSuper.kt");
}
@Test
@TestMetadata("typeUsageWithImplicitType.kt")
public void testTypeUsageWithImplicitType() throws Exception {
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.getRetention
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isActual
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
@@ -26,6 +25,7 @@ import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
import org.jetbrains.kotlin.types.model.TypeSystemContext
import org.jetbrains.kotlin.utils.zipIfSizesAreEqual
class FirExpectActualMatchingContextImpl private constructor(
private val actualSession: FirSession,
@@ -160,6 +161,9 @@ class FirExpectActualMatchingContextImpl private constructor(
override val RegularClassSymbolMarker.superTypes: List<KotlinTypeMarker>
get() = asSymbol().resolvedSuperTypes
override val RegularClassSymbolMarker.superTypesRefs: List<TypeRefMarker>
get() = asSymbol().resolvedSuperTypeRefs
override val RegularClassSymbolMarker.defaultType: KotlinTypeMarker
get() = asSymbol().defaultType()
@@ -245,8 +249,12 @@ class FirExpectActualMatchingContextImpl private constructor(
get() = asSymbol().dispatchReceiverType
override val CallableSymbolMarker.extensionReceiverType: KotlinTypeMarker?
get() = asSymbol().resolvedReceiverTypeRef?.coneType
override val CallableSymbolMarker.extensionReceiverTypeRef: TypeRefMarker?
get() = asSymbol().resolvedReceiverTypeRef
override val CallableSymbolMarker.returnType: KotlinTypeMarker
get() = asSymbol().resolvedReturnType.type
override val CallableSymbolMarker.returnTypeRef: TypeRefMarker
get() = asSymbol().resolvedReturnTypeRef
override val CallableSymbolMarker.typeParameters: List<TypeParameterSymbolMarker>
get() = asSymbol().typeParameterSymbols
@@ -286,6 +294,9 @@ class FirExpectActualMatchingContextImpl private constructor(
override val TypeParameterSymbolMarker.bounds: List<KotlinTypeMarker>
get() = asSymbol().resolvedBounds.map { it.coneType }
override val TypeParameterSymbolMarker.boundsTypeRefs: List<TypeRefMarker>
get() = asSymbol().resolvedBounds
override val TypeParameterSymbolMarker.variance: Variance
get() = asSymbol().variance
@@ -489,6 +500,65 @@ class FirExpectActualMatchingContextImpl private constructor(
override fun DeclarationSymbolMarker.getSourceElement(): SourceElementMarker = FirSourceElement(asSymbol().source)
override fun TypeRefMarker.getClassId(): ClassId? = (this as FirResolvedTypeRef).type.fullyExpandedType(actualSession).classId
override fun checkAnnotationsOnTypeRefAndArguments(
expectTypeRef: TypeRefMarker,
actualTypeRef: TypeRefMarker,
checker: ExpectActualMatchingContext.AnnotationsCheckerCallback,
) {
check(expectTypeRef is FirResolvedTypeRef && actualTypeRef is FirResolvedTypeRef)
checkAnnotationsOnTypeRefAndArgumentsImpl(expectTypeRef, actualTypeRef, checker)
}
private fun checkAnnotationsOnTypeRefAndArgumentsImpl(
expectTypeRef: FirTypeRef?,
actualTypeRef: FirTypeRef?,
checker: ExpectActualMatchingContext.AnnotationsCheckerCallback,
) {
fun FirAnnotationContainer.getAnnotations() = annotations.map(::AnnotationCallInfoImpl)
if (expectTypeRef == null || actualTypeRef == null) return
if (expectTypeRef is FirErrorTypeRef || actualTypeRef is FirErrorTypeRef) return
checker.check(expectTypeRef.getAnnotations(), actualTypeRef.getAnnotations(), FirSourceElement(actualTypeRef.source))
val expectDelegatedTypeRef = (expectTypeRef as? FirResolvedTypeRef)?.delegatedTypeRef ?: return
val actualDelegatedTypeRef = (actualTypeRef as? FirResolvedTypeRef?)?.delegatedTypeRef ?: return
when {
expectDelegatedTypeRef is FirUserTypeRef && actualDelegatedTypeRef is FirUserTypeRef -> {
val expectQualifier = expectDelegatedTypeRef.qualifier
val actualQualifier = actualDelegatedTypeRef.qualifier
for ((expectPart, actualPart) in expectQualifier.zipIfSizesAreEqual(actualQualifier).orEmpty()) {
val expectPartTypeArguments = expectPart.typeArgumentList.typeArguments
val actualPartTypeArguments = actualPart.typeArgumentList.typeArguments
val zippedArgs = expectPartTypeArguments.zipIfSizesAreEqual(actualPartTypeArguments).orEmpty()
for ((expectTypeArgument, actualTypeArgument) in zippedArgs) {
if (expectTypeArgument !is FirTypeProjectionWithVariance || actualTypeArgument !is FirTypeProjectionWithVariance) {
continue
}
checkAnnotationsOnTypeRefAndArgumentsImpl(expectTypeArgument.typeRef, actualTypeArgument.typeRef, checker)
}
}
}
expectDelegatedTypeRef is FirFunctionTypeRef && actualDelegatedTypeRef is FirFunctionTypeRef -> {
checkAnnotationsOnTypeRefAndArgumentsImpl(
expectDelegatedTypeRef.receiverTypeRef, actualDelegatedTypeRef.receiverTypeRef, checker,
)
checkAnnotationsOnTypeRefAndArgumentsImpl(
expectDelegatedTypeRef.returnTypeRef, actualDelegatedTypeRef.returnTypeRef, checker,
)
val expectParams = expectDelegatedTypeRef.parameters
val actualParams = actualDelegatedTypeRef.parameters
for ((expectParam, actualParam) in expectParams.zipIfSizesAreEqual(actualParams).orEmpty()) {
checkAnnotationsOnTypeRefAndArgumentsImpl(expectParam.returnTypeRef, actualParam.returnTypeRef, checker)
}
}
}
}
object Factory : FirExpectActualMatchingContextFactory {
override fun create(
session: FirSession, scopeSession: ScopeSession,
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.mpp.TypeRefMarker
import org.jetbrains.kotlin.fir.visitors.*
/*
@@ -15,7 +16,7 @@ import org.jetbrains.kotlin.fir.visitors.*
* DO NOT MODIFY IT MANUALLY
*/
abstract class FirResolvedTypeRef : FirTypeRef() {
abstract class FirResolvedTypeRef : FirTypeRef(), TypeRefMarker {
abstract override val source: KtSourceElement?
abstract override val annotations: List<FirAnnotation>
abstract val type: ConeKotlinType
@@ -707,7 +707,7 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
resolvedTypeRef.configure {
+field("type", coneKotlinTypeType)
+field("delegatedTypeRef", typeRef, nullable = true)
// TODO: add supertype in subsequent commits
element.additionalSupertypeInterfaces.add(typeRefMarkerType)
}
typeRefWithNullability.configure {
@@ -108,3 +108,5 @@ val firPropertySymbolType = type("fir.symbols.impl", "FirPropertySymbol")
val errorTypeRefImplType = type("fir.types.impl", "FirErrorTypeRefImpl")
val annotationResolvePhaseType = generatedType("expressions", "FirAnnotationResolvePhase")
val typeRefMarkerType = type("mpp", "TypeRefMarker")
@@ -28,7 +28,6 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
import org.jetbrains.kotlin.mpp.SourceElementMarker
import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.types.AbstractTypeChecker
@@ -177,9 +176,12 @@ internal abstract class IrExpectActualMatchingContext(
else -> shouldNotBeCalled()
}
override val RegularClassSymbolMarker.superTypes: List<KotlinTypeMarker>
override val RegularClassSymbolMarker.superTypes: List<IrType>
get() = asIr().superTypes
override val RegularClassSymbolMarker.superTypesRefs: List<TypeRefMarker>
get() = superTypes
override val RegularClassSymbolMarker.defaultType: KotlinTypeMarker
get() = asIr().defaultType
@@ -275,10 +277,13 @@ internal abstract class IrExpectActualMatchingContext(
override val CallableSymbolMarker.dispatchReceiverType: KotlinTypeMarker?
get() = (asIr().parent as? IrClass)?.defaultType
override val CallableSymbolMarker.extensionReceiverType: KotlinTypeMarker?
override val CallableSymbolMarker.extensionReceiverType: IrType?
get() = safeAsIr<IrFunction>()?.extensionReceiverParameter?.type
override val CallableSymbolMarker.returnType: KotlinTypeMarker
override val CallableSymbolMarker.extensionReceiverTypeRef: TypeRefMarker?
get() = extensionReceiverType
override val CallableSymbolMarker.returnType: IrType
get() = processIr(
onFunction = { it.returnType },
onProperty = { it.getter?.returnType ?: it.backingField?.type ?: error("No type for property: $it") },
@@ -287,6 +292,10 @@ internal abstract class IrExpectActualMatchingContext(
onEnumEntry = { it.parentAsClass.defaultType }
)
override val CallableSymbolMarker.returnTypeRef: TypeRefMarker
get() = returnType
override val CallableSymbolMarker.typeParameters: List<TypeParameterSymbolMarker>
get() = processIr(
onFunction = { it.typeParameters.map { parameter -> parameter.symbol } },
@@ -316,8 +325,10 @@ internal abstract class IrExpectActualMatchingContext(
return irConstructor.constructedClass.isAnnotationClass
}
override val TypeParameterSymbolMarker.bounds: List<KotlinTypeMarker>
override val TypeParameterSymbolMarker.bounds: List<IrType>
get() = asIr().superTypes
override val TypeParameterSymbolMarker.boundsTypeRefs: List<TypeRefMarker>
get() = bounds
override val TypeParameterSymbolMarker.variance: Variance
get() = asIr().variance
override val TypeParameterSymbolMarker.isReified: Boolean
@@ -529,4 +540,27 @@ internal abstract class IrExpectActualMatchingContext(
private object IrSourceElementStub : SourceElementMarker
override fun DeclarationSymbolMarker.getSourceElement(): SourceElementMarker = IrSourceElementStub
override fun TypeRefMarker.getClassId(): ClassId? = (this as IrType).getClass()?.classId
override fun checkAnnotationsOnTypeRefAndArguments(
expectTypeRef: TypeRefMarker,
actualTypeRef: TypeRefMarker,
checker: ExpectActualMatchingContext.AnnotationsCheckerCallback
) {
check(expectTypeRef is IrType && actualTypeRef is IrType)
fun IrType.getAnnotations() = annotations.map(::AnnotationCallInfoImpl)
checker.check(expectTypeRef.getAnnotations(), actualTypeRef.getAnnotations(), IrSourceElementStub)
if (expectTypeRef !is IrSimpleType || actualTypeRef !is IrSimpleType) return
if (expectTypeRef.arguments.size != actualTypeRef.arguments.size) return
for ((expectArg, actualArg) in expectTypeRef.arguments.zip(actualTypeRef.arguments)) {
val expectArgType = expectArg.typeOrNull ?: continue
val actualArgType = actualArg.typeOrNull ?: continue
checkAnnotationsOnTypeRefAndArguments(expectArgType, actualArgType, checker)
}
}
}
@@ -10,11 +10,12 @@ import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeAliasSymbol
import org.jetbrains.kotlin.ir.types.impl.IrTypeBase
import org.jetbrains.kotlin.mpp.TypeRefMarker
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.model.*
abstract class IrType : KotlinTypeMarker, IrAnnotationContainer {
abstract class IrType : KotlinTypeMarker, TypeRefMarker, IrAnnotationContainer {
/**
* @return true if this type is equal to [other] symbolically. Note that this is NOT EQUIVALENT to the full type checking algorithm
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.utils.zipIfSizesAreEqual
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualAnnotationsIncompatibilityType as IncompatibilityType
object AbstractExpectActualAnnotationMatchChecker {
@@ -84,6 +85,13 @@ object AbstractExpectActualAnnotationMatchChecker {
arePropertyGetterAndSetterAnnotationsCompatible(expectSymbol, actualSymbol)?.let { return it }
}
areAnnotationsSetOnTypesCompatible(
expectSymbol, actualSymbol, expectSymbol.extensionReceiverTypeRef, actualSymbol.extensionReceiverTypeRef
)?.let { return it }
areAnnotationsSetOnTypesCompatible(expectSymbol, actualSymbol, expectSymbol.returnTypeRef, actualSymbol.returnTypeRef)
?.let { return it }
return null
}
@@ -126,6 +134,16 @@ object AbstractExpectActualAnnotationMatchChecker {
checkAnnotationsOnEnumEntries(expectSymbol, actualSymbol)?.let { return it }
}
// actual may have more super types, that's why we can't just zip them.
// Identifying super type with ClassId (without type parameters) is enough because same type can't be extended twice.
val actualSuperTypes = actualSymbol.superTypesRefs.groupBy { it.getClassId() }
for (expectSuperType in expectSymbol.superTypesRefs) {
val expectClassId = expectSuperType.getClassId() ?: continue
val actualSuperType = actualSuperTypes[expectClassId]?.singleOrNull() ?: continue
areAnnotationsSetOnTypesCompatible(expectSymbol, actualSymbol, expectSuperType, actualSuperType)
?.let { return it }
}
return null
}
@@ -148,13 +166,12 @@ object AbstractExpectActualAnnotationMatchChecker {
val expectParams = expectSymbol.valueParameters
val actualParams = actualSymbol.valueParameters
if (expectParams.size != actualParams.size) return null
return expectParams.zip(actualParams).firstNotNullOfOrNull { (expectParam, actualParam) ->
return expectParams.zipIfSizesAreEqual(actualParams)?.firstNotNullOfOrNull { (expectParam, actualParam) ->
areAnnotationsSetOnDeclarationsCompatible(expectParam, actualParam)?.let {
// Write containing declarations into diagnostic
Incompatibility(expectSymbol, actualSymbol, actualParam.getSourceElement(), it.type)
}
?: areAnnotationsSetOnTypesCompatible(expectSymbol, actualSymbol, expectParam.returnTypeRef, actualParam.returnTypeRef)
}
}
@@ -173,16 +190,57 @@ object AbstractExpectActualAnnotationMatchChecker {
val expectParams = expectSymbol.getTypeParameters() ?: return null
val actualParams = actualSymbol.getTypeParameters() ?: return null
if (expectParams.size != actualParams.size) return null
return expectParams.zip(actualParams).firstNotNullOfOrNull { (expectParam, actualParam) ->
return expectParams.zipIfSizesAreEqual(actualParams)?.firstNotNullOfOrNull { (expectParam, actualParam) ->
areAnnotationsSetOnDeclarationsCompatible(expectParam, actualParam)?.let {
// Write containing declarations into diagnostic
Incompatibility(expectSymbol, actualSymbol, actualParam.getSourceElement(), it.type)
}
?: areAnnotationsOnTypeParameterBoundsCompatible(expectSymbol, actualSymbol, expectParam, actualParam)
}
}
context (ExpectActualMatchingContext<*>)
private fun areAnnotationsOnTypeParameterBoundsCompatible(
expectDeclarationSymbol: DeclarationSymbolMarker,
actualDeclarationSymbol: DeclarationSymbolMarker,
expectParam: TypeParameterSymbolMarker,
actualParam: TypeParameterSymbolMarker,
): Incompatibility? {
val expectBounds = expectParam.boundsTypeRefs
val actualBounds = actualParam.boundsTypeRefs
return expectBounds.zipIfSizesAreEqual(actualBounds)?.firstNotNullOfOrNull { (expectType, actualType) ->
// Identifying bounds by ClassId (without type parameters) is enough because type can't have two bounds of same type.
if (expectType.getClassId() != actualType.getClassId()) {
// Expect and actual type bounds must be same and have same order, otherwise expect-actual compatibility checker
// reports error. So, skip in case of incompatibility to not report extra warning.
return@firstNotNullOfOrNull null
}
areAnnotationsSetOnTypesCompatible(expectDeclarationSymbol, actualDeclarationSymbol, expectType, actualType)
}
}
context (ExpectActualMatchingContext<*>)
private fun areAnnotationsSetOnTypesCompatible(
expectDeclarationSymbol: DeclarationSymbolMarker,
actualDeclarationSymbol: DeclarationSymbolMarker,
expectTypeRef: TypeRefMarker?,
actualTypeRef: TypeRefMarker?,
): Incompatibility? {
if (expectTypeRef == null || actualTypeRef == null) return null
var firstIncompatibility: Incompatibility? = null
checkAnnotationsOnTypeRefAndArguments(expectTypeRef, actualTypeRef) { expectAnnotations, actualAnnotations, actualTypeRefSource ->
if (firstIncompatibility == null) {
firstIncompatibility = areAnnotationListsCompatible(expectAnnotations, actualAnnotations, actualDeclarationSymbol)
?.let { Incompatibility(expectDeclarationSymbol, actualDeclarationSymbol, actualTypeRefSource, type = it) }
}
}
return firstIncompatibility
}
context (ExpectActualMatchingContext<*>)
private fun areAnnotationsSetOnDeclarationsCompatible(
expectSymbol: DeclarationSymbolMarker,
@@ -198,7 +256,6 @@ object AbstractExpectActualAnnotationMatchChecker {
actualAnnotations: List<ExpectActualMatchingContext.AnnotationCallInfo>,
actualContainerSymbol: DeclarationSymbolMarker,
): IncompatibilityType<ExpectActualMatchingContext.AnnotationCallInfo>? {
// TODO(Roman.Efremov, KT-60671): check annotations set on types
val skipSourceAnnotations = actualContainerSymbol.hasSourceAnnotationsErased
val actualAnnotationsByName = actualAnnotations.groupBy { it.classId }
@@ -85,6 +85,7 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
val CallableSymbolMarker.visibility: Visibility
val RegularClassSymbolMarker.superTypes: List<KotlinTypeMarker>
val RegularClassSymbolMarker.superTypesRefs: List<TypeRefMarker>
val RegularClassSymbolMarker.defaultType: KotlinTypeMarker
val CallableSymbolMarker.isExpect: Boolean
@@ -115,7 +116,9 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
val CallableSymbolMarker.dispatchReceiverType: KotlinTypeMarker?
val CallableSymbolMarker.extensionReceiverType: KotlinTypeMarker?
val CallableSymbolMarker.extensionReceiverTypeRef: TypeRefMarker?
val CallableSymbolMarker.returnType: KotlinTypeMarker
val CallableSymbolMarker.returnTypeRef: TypeRefMarker
val CallableSymbolMarker.typeParameters: List<TypeParameterSymbolMarker>
val FunctionSymbolMarker.valueParameters: List<ValueParameterSymbolMarker>
@@ -135,6 +138,7 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
fun CallableSymbolMarker.isAnnotationConstructor(): Boolean
val TypeParameterSymbolMarker.bounds: List<KotlinTypeMarker>
val TypeParameterSymbolMarker.boundsTypeRefs: List<TypeRefMarker>
val TypeParameterSymbolMarker.variance: Variance
val TypeParameterSymbolMarker.isReified: Boolean
@@ -215,4 +219,35 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
): Map<out DeclarationSymbolMarker, ExpectActualCompatibility<*>>
fun DeclarationSymbolMarker.getSourceElement(): SourceElementMarker
fun TypeRefMarker.getClassId(): ClassId?
/**
* Callback interface to be implemented by caller of [checkAnnotationsOnTypeRefAndArguments].
*/
fun interface AnnotationsCheckerCallback {
/**
* Implementation must check `expect` and `actual` annotations and report diagnostic in case of incompatibility.
* [actualTypeRefSource] is needed in order to know where on the `actual` declaration to insert the missing annotation
* from the `expect` declaration (see [AbstractExpectActualAnnotationMatchChecker.Incompatibility.actualAnnotationTargetElement]).
*/
fun check(
expectAnnotations: List<AnnotationCallInfo>, actualAnnotations: List<AnnotationCallInfo>,
actualTypeRefSource: SourceElementMarker,
)
}
/**
* Finds pairs of matching expect and actual types, on which annotations must be checked by [AbstractExpectActualAnnotationMatchChecker].
*
* This is done by recursively traversing [expectTypeRef] and [actualTypeRef] and their arguments, which is needed in case of
* complex types like `T1<T2<@Ann T3>>`. Founded expect and actual annotations are passed to [checker] callback.
* For functional types (e.g. `ReceiverType.(Arg1Type) -> ReturnType`) receiver, argument and return types and their arguments
* are checked.
*
* **Example**: for type `@Ann1 List<@Ann2 Map<@Ann3 Int, @Ann4 String>>`, there are 4 types to check in [checker].
*/
fun checkAnnotationsOnTypeRefAndArguments(
expectTypeRef: TypeRefMarker, actualTypeRef: TypeRefMarker, checker: AnnotationsCheckerCallback,
)
}
@@ -179,8 +179,6 @@ object K1AbstractExpectActualAnnotationMatchChecker {
expectSymbol: DeclarationSymbolMarker,
actualSymbol: DeclarationSymbolMarker,
): Incompatibility? {
// TODO(Roman.Efremov, KT-60671): check annotations set on types
val skipSourceAnnotations = actualSymbol.hasSourceAnnotationsErased
val actualAnnotationsByName = actualSymbol.annotations.groupBy { it.classId }
@@ -21,3 +21,6 @@ All annotations from expect 'fun <T> inTypeParam(): Unit' must be present with t
/jvm.kt:(513,521): warning: Annotation `@PROPERTY_GETTER:Ann()` is missing on actual declaration.
All annotations from expect 'val onGetter: String get(): String' must be present with the same arguments on actual 'val onGetter: String get(): String', otherwise they might behave incorrectly.
/jvm.kt:(547,553): warning: Annotation `@Ann()` is missing on actual declaration.
All annotations from expect 'fun onType(param: @R|Ann|() Any): Unit' must be present with the same arguments on actual 'fun onType(param: Any): Unit', otherwise they might behave incorrectly.
@@ -68,4 +68,4 @@ actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>withDifferentArg<!>() {}<!>
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT, ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual val <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>onGetter<!>: String = ""<!>
actual fun onType(param: Any) {}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>onType<!>(param: Any) {}<!>
@@ -61,46 +61,46 @@ expect fun funcTypeArgType(arg: (arg: @Ann Any) -> Unit)
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
actual fun valueParameterType(arg: String) {}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>valueParameterType<!>(arg: String) {}<!>
actual fun returnType(): String = ""
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>returnType<!>(): String = ""<!>
actual fun <T : Any> methodTypeParamBound() {}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <T : Any> <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>methodTypeParamBound<!>() {}<!>
actual class OnClassTypeParamBound<T : Any>
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual class <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>OnClassTypeParamBound<!><T : Any><!>
actual fun <T> typeParamBoundInWhere() where T : Any {}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <T> <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>typeParamBoundInWhere<!>() where T : Any {}<!>
actual fun <T> severalBounds() where T : I1, T : I2 {}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <T> <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>severalBounds<!>() where T : I1, T : I2 {}<!>
actual fun <T> <!ACTUAL_WITHOUT_EXPECT!>severalBoundsDifferentOrder<!>() where T : @Ann I1, T : I2 {}
actual fun <T> <!ACTUAL_WITHOUT_EXPECT!>lessTypeParamBoundsOnActual<!>() where T : @Ann I2 {}
actual fun Any.onReceiver() {}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun Any.<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>onReceiver<!>() {}<!>
actual class OnClassSuper : I1
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual class <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>OnClassSuper<!> : I1<!>
actual class OnClassSuperDifferentOrder : @Ann I2, I1
actual class OnClassSuperMoreOnActual : I1, I2
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual class <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>OnClassSuperMoreOnActual<!> : I1, I2<!>
actual class OnClassSuperTypeParams<T> : I3<T>
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual class <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>OnClassSuperTypeParams<!><T> : I3<T><!>
actual fun deepInParamsTypes(arg: I3<I3<Any>>) {}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>deepInParamsTypes<!>(arg: I3<I3<Any>>) {}<!>
actual fun starProjection(arg: I4<*, Any>) {}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>starProjection<!>(arg: I4<*, Any>) {}<!>
actual fun <T> typeArgWithVariance(t: I3<out T>) {}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <T> <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>typeArgWithVariance<!>(t: I3<out T>) {}<!>
actual fun qualifierPartsMatching(arg: WithNested<String>.Nested<@Ann String>) {}
actual fun qualifierPartsNonMatching(arg: WithNested<@Ann String>.Nested<String>) {}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>qualifierPartsNonMatching<!>(arg: WithNested<@Ann String>.Nested<String>) {}<!>
actual fun <!ACTUAL_WITHOUT_EXPECT!>funTypeVsUserType<!>(arg: kotlin.jvm.functions.Function0<String>) {}
actual fun funcTypeReturnType(arg: () -> Any) {}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>funcTypeReturnType<!>(arg: () -> Any) {}<!>
actual fun funcTypeReceiverType(arg: Any.() -> Unit) {}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>funcTypeReceiverType<!>(arg: Any.() -> Unit) {}<!>
actual fun funcTypeArgType(arg: (arg: Any) -> Unit) {}
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>funcTypeArgType<!>(arg: (arg: Any) -> Unit) {}<!>
@@ -0,0 +1,14 @@
// MODULE: m1-common
// FILE: common.kt
@Target(AnnotationTarget.TYPE)
annotation class Ann
interface I
expect class Foo: @Ann I
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
typealias ITypealias = I
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual class <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>Foo<!> : ITypealias<!>
@@ -0,0 +1,14 @@
// MODULE: m1-common
// FILE: common.kt
@Target(AnnotationTarget.TYPE)
annotation class Ann
interface I
expect class Foo: @Ann I
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
typealias ITypealias = I
actual class Foo : ITypealias
@@ -0,0 +1,10 @@
// MODULE: m1-common
// FILE: common.kt
@Target(AnnotationTarget.TYPE)
annotation class Ann
expect fun foo(): @Ann Int
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>foo<!>() = 1<!>
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// MODULE: m1-common
// FILE: common.kt
@Target(AnnotationTarget.TYPE)
@@ -12,7 +12,7 @@ expect class E2 {
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
interface I {
fun f(x: Int = 5) = x
fun f(x: Int = 5): Int = x
}
actual class <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>E<!>(i: I) : I by i
@@ -23,11 +23,11 @@ expect class FastArrayList<E> : MutableListEx<E>, RandomAccess {
fun ensureCapacity(minCapacity: Int)
override val size: Int
override fun isEmpty(): Boolean
override fun contains(element: @UnsafeVariance E): Boolean
override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
override fun contains(element: E): Boolean
override fun containsAll(elements: Collection<E>): Boolean
override operator fun get(index: Int): E
override fun indexOf(element: @UnsafeVariance E): Int
override fun lastIndexOf(element: @UnsafeVariance E): Int
override fun indexOf(element: E): Int
override fun lastIndexOf(element: E): Int
override fun iterator(): MutableIterator<E>
override fun add(element: E): Boolean
override fun remove(element: E): Boolean
@@ -24107,6 +24107,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/typeUsage.kt");
}
@Test
@TestMetadata("typeUsageTypealiasInSuper.kt")
public void testTypeUsageTypealiasInSuper() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/annotationMatching/typeUsageTypealiasInSuper.kt");
}
@Test
@TestMetadata("typeUsageWithImplicitType.kt")
public void testTypeUsageWithImplicitType() throws Exception {
@@ -0,0 +1,17 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.mpp
import org.jetbrains.kotlin.name.ClassId
/**
* Common interface for type references to be used in abstract checker.
* The idea is similar to [org.jetbrains.kotlin.types.model.KotlinTypeMarker],
* but type reference, unlike a type, has source element.
*
* Used in [org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualAnnotationMatchChecker].
*/
interface TypeRefMarker