[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:
committed by
Space Team
parent
cb8529d65b
commit
423f4ca5f0
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+71
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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")
|
||||
|
||||
+39
-5
@@ -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
|
||||
|
||||
+63
-6
@@ -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 }
|
||||
|
||||
|
||||
+35
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
-2
@@ -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 }
|
||||
|
||||
|
||||
+3
@@ -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.
|
||||
|
||||
Vendored
+1
-1
@@ -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) {}<!>
|
||||
|
||||
+17
-17
@@ -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) {}<!>
|
||||
|
||||
+14
@@ -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<!>
|
||||
Vendored
+14
@@ -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
|
||||
+10
@@ -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<!>
|
||||
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
@Target(AnnotationTarget.TYPE)
|
||||
|
||||
Vendored
+1
-1
@@ -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
|
||||
|
||||
+4
-4
@@ -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
|
||||
|
||||
Generated
+6
@@ -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
|
||||
Reference in New Issue
Block a user