[FIR] Fix case with lazy resolve in expect-actual annotation checker
Unresolved annotation arguments were treated as absent arguments, which lead to false-positive reports. Add assert and test for that and fix. MR: KT-MR-12245 ^KT-60671 Fixed
This commit is contained in:
committed by
Space Team
parent
4c75fb108f
commit
0fd700de21
+6
@@ -1652,6 +1652,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/sourceRetentionAnnotationsWhenTypealias.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typealiasToJavaWithAnnotationArgument.kt")
|
||||
public void testTypealiasToJavaWithAnnotationArgument() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasToJavaWithAnnotationArgument.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typealiasedAnnotation.kt")
|
||||
public void testTypealiasedAnnotation() throws Exception {
|
||||
|
||||
+6
@@ -1652,6 +1652,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/sourceRetentionAnnotationsWhenTypealias.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typealiasToJavaWithAnnotationArgument.kt")
|
||||
public void testTypealiasToJavaWithAnnotationArgument() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasToJavaWithAnnotationArgument.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typealiasedAnnotation.kt")
|
||||
public void testTypealiasedAnnotation() throws Exception {
|
||||
|
||||
+34
-8
@@ -11,14 +11,13 @@ import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isActual
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.resolve.*
|
||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||
import org.jetbrains.kotlin.fir.scopes.*
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.symbols.resolvedAnnotationsWithArguments
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.mpp.*
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
@@ -364,6 +363,13 @@ class FirExpectActualMatchingContextImpl private constructor(
|
||||
}
|
||||
|
||||
private fun areFirAnnotationsEqual(annotation1: FirAnnotation, annotation2: FirAnnotation): Boolean {
|
||||
fun FirAnnotation.hasResolvedArguments(): Boolean {
|
||||
return resolved || (this is FirAnnotationCall && arguments.isEmpty())
|
||||
}
|
||||
|
||||
check(annotation1.hasResolvedArguments() && annotation2.hasResolvedArguments()) {
|
||||
"By this time compared annotations are expected to have resolved arguments"
|
||||
}
|
||||
if (!areCompatibleExpectActualTypes(annotation1.resolvedType, annotation2.resolvedType)) {
|
||||
return false
|
||||
}
|
||||
@@ -503,25 +509,37 @@ class FirExpectActualMatchingContextImpl private constructor(
|
||||
override fun TypeRefMarker.getClassId(): ClassId? = (this as FirResolvedTypeRef).type.fullyExpandedType(actualSession).classId
|
||||
|
||||
override fun checkAnnotationsOnTypeRefAndArguments(
|
||||
expectContainingSymbol: DeclarationSymbolMarker,
|
||||
actualContainingSymbol: DeclarationSymbolMarker,
|
||||
expectTypeRef: TypeRefMarker,
|
||||
actualTypeRef: TypeRefMarker,
|
||||
checker: ExpectActualMatchingContext.AnnotationsCheckerCallback,
|
||||
) {
|
||||
check(expectTypeRef is FirResolvedTypeRef && actualTypeRef is FirResolvedTypeRef)
|
||||
checkAnnotationsOnTypeRefAndArgumentsImpl(expectTypeRef, actualTypeRef, checker)
|
||||
checkAnnotationsOnTypeRefAndArgumentsImpl(
|
||||
expectContainingSymbol.asSymbol(), actualContainingSymbol.asSymbol(),
|
||||
expectTypeRef, actualTypeRef, checker
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkAnnotationsOnTypeRefAndArgumentsImpl(
|
||||
expectContainingSymbol: FirBasedSymbol<*>,
|
||||
actualContainingSymbol: FirBasedSymbol<*>,
|
||||
expectTypeRef: FirTypeRef?,
|
||||
actualTypeRef: FirTypeRef?,
|
||||
checker: ExpectActualMatchingContext.AnnotationsCheckerCallback,
|
||||
) {
|
||||
fun FirAnnotationContainer.getAnnotations() = annotations.map(::AnnotationCallInfoImpl)
|
||||
fun FirAnnotationContainer.getAnnotations(anchor: FirBasedSymbol<*>): List<AnnotationCallInfoImpl> {
|
||||
return resolvedAnnotationsWithArguments(anchor).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))
|
||||
checker.check(
|
||||
expectTypeRef.getAnnotations(expectContainingSymbol), actualTypeRef.getAnnotations(actualContainingSymbol),
|
||||
FirSourceElement(actualTypeRef.source)
|
||||
)
|
||||
|
||||
val expectDelegatedTypeRef = (expectTypeRef as? FirResolvedTypeRef)?.delegatedTypeRef ?: return
|
||||
val actualDelegatedTypeRef = (actualTypeRef as? FirResolvedTypeRef?)?.delegatedTypeRef ?: return
|
||||
@@ -538,22 +556,30 @@ class FirExpectActualMatchingContextImpl private constructor(
|
||||
if (expectTypeArgument !is FirTypeProjectionWithVariance || actualTypeArgument !is FirTypeProjectionWithVariance) {
|
||||
continue
|
||||
}
|
||||
checkAnnotationsOnTypeRefAndArgumentsImpl(expectTypeArgument.typeRef, actualTypeArgument.typeRef, checker)
|
||||
checkAnnotationsOnTypeRefAndArgumentsImpl(
|
||||
expectContainingSymbol, actualContainingSymbol,
|
||||
expectTypeArgument.typeRef, actualTypeArgument.typeRef, checker
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
expectDelegatedTypeRef is FirFunctionTypeRef && actualDelegatedTypeRef is FirFunctionTypeRef -> {
|
||||
checkAnnotationsOnTypeRefAndArgumentsImpl(
|
||||
expectContainingSymbol, actualContainingSymbol,
|
||||
expectDelegatedTypeRef.receiverTypeRef, actualDelegatedTypeRef.receiverTypeRef, checker,
|
||||
)
|
||||
checkAnnotationsOnTypeRefAndArgumentsImpl(
|
||||
expectContainingSymbol, actualContainingSymbol,
|
||||
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)
|
||||
checkAnnotationsOnTypeRefAndArgumentsImpl(
|
||||
expectContainingSymbol, actualContainingSymbol,
|
||||
expectParam.returnTypeRef, actualParam.returnTypeRef, checker
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -544,6 +544,8 @@ internal abstract class IrExpectActualMatchingContext(
|
||||
override fun TypeRefMarker.getClassId(): ClassId? = (this as IrType).getClass()?.classId
|
||||
|
||||
override fun checkAnnotationsOnTypeRefAndArguments(
|
||||
expectContainingSymbol: DeclarationSymbolMarker,
|
||||
actualContainingSymbol: DeclarationSymbolMarker,
|
||||
expectTypeRef: TypeRefMarker,
|
||||
actualTypeRef: TypeRefMarker,
|
||||
checker: ExpectActualMatchingContext.AnnotationsCheckerCallback
|
||||
@@ -560,7 +562,7 @@ internal abstract class IrExpectActualMatchingContext(
|
||||
for ((expectArg, actualArg) in expectTypeRef.arguments.zip(actualTypeRef.arguments)) {
|
||||
val expectArgType = expectArg.typeOrNull ?: continue
|
||||
val actualArgType = actualArg.typeOrNull ?: continue
|
||||
checkAnnotationsOnTypeRefAndArguments(expectArgType, actualArgType, checker)
|
||||
checkAnnotationsOnTypeRefAndArguments(expectContainingSymbol, actualContainingSymbol, expectArgType, actualArgType, checker)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -232,7 +232,10 @@ object AbstractExpectActualAnnotationMatchChecker {
|
||||
|
||||
var firstIncompatibility: Incompatibility? = null
|
||||
|
||||
checkAnnotationsOnTypeRefAndArguments(expectTypeRef, actualTypeRef) { expectAnnotations, actualAnnotations, actualTypeRefSource ->
|
||||
checkAnnotationsOnTypeRefAndArguments(
|
||||
expectDeclarationSymbol, actualDeclarationSymbol,
|
||||
expectTypeRef, actualTypeRef
|
||||
) { expectAnnotations, actualAnnotations, actualTypeRefSource ->
|
||||
if (firstIncompatibility == null) {
|
||||
firstIncompatibility = areAnnotationListsCompatible(expectAnnotations, actualAnnotations, actualDeclarationSymbol)
|
||||
?.let { Incompatibility(expectDeclarationSymbol, actualDeclarationSymbol, actualTypeRefSource, type = it) }
|
||||
|
||||
+5
-1
@@ -248,6 +248,10 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
|
||||
* **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,
|
||||
expectContainingSymbol: DeclarationSymbolMarker,
|
||||
actualContainingSymbol: DeclarationSymbolMarker,
|
||||
expectTypeRef: TypeRefMarker,
|
||||
actualTypeRef: TypeRefMarker,
|
||||
checker: AnnotationsCheckerCallback,
|
||||
)
|
||||
}
|
||||
|
||||
Vendored
+4
-2
@@ -1,5 +1,7 @@
|
||||
// LL_FIR_DIVERGENCE
|
||||
// The reason is bug, which will be fixed in next commit
|
||||
// Difference to FIR is false-positive report of MISSING_DEPENDENCY_SUPERCLASS.
|
||||
// This is because JVM Enum with `java.io.Serializable` superclass resolved instead of common builtin Enum.
|
||||
// Must be fixed with KT-61757.
|
||||
// LL_FIR_DIVERGENCE
|
||||
|
||||
// Test for ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT diagnostic when annotations arguments are lazily resolved.
|
||||
@@ -25,7 +27,7 @@ expect fun withEmptyArguments_positive()
|
||||
|
||||
// MODULE: main()()(common)
|
||||
// TARGET_PLATFORM: JVM
|
||||
actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>onType_negative<!>(): @Ann("") Any = Any()
|
||||
actual fun onType_negative(): @Ann("") Any = Any()
|
||||
actual fun <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>onType_positive<!>(): @Ann("incorrect") Any = Any()
|
||||
|
||||
@Ann("")
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// This test is for the case when expect annotation is FirAnnotationCall and actual annotation is not FirAnnotationCall
|
||||
// MODULE: common
|
||||
// TARGET_PLATFORM: Common
|
||||
expect annotation class Ann(val p: Int)
|
||||
|
||||
@Ann(p = 1)
|
||||
expect class Foo
|
||||
|
||||
// MODULE: main()()(common)
|
||||
// TARGET_PLATFORM: JVM
|
||||
// FILE: Foo.kt
|
||||
actual annotation class Ann actual constructor(actual val p: Int)
|
||||
|
||||
<!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>actual typealias <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>Foo<!> = FooImpl<!>
|
||||
|
||||
// FILE: FooImpl.java
|
||||
@Ann(p = 2)
|
||||
public class FooImpl {
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// This test is for the case when expect annotation is FirAnnotationCall and actual annotation is not FirAnnotationCall
|
||||
// MODULE: common
|
||||
// TARGET_PLATFORM: Common
|
||||
expect annotation class Ann(val p: Int)
|
||||
|
||||
@Ann(p = 1)
|
||||
expect class Foo
|
||||
|
||||
// MODULE: main()()(common)
|
||||
// TARGET_PLATFORM: JVM
|
||||
// FILE: Foo.kt
|
||||
actual annotation class Ann actual constructor(actual val p: Int)
|
||||
|
||||
actual typealias <!ACTUAL_ANNOTATIONS_NOT_MATCH_EXPECT!>Foo<!> = FooImpl
|
||||
|
||||
// FILE: FooImpl.java
|
||||
@Ann(p = 2)
|
||||
public class FooImpl {
|
||||
}
|
||||
Generated
+6
@@ -24833,6 +24833,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/sourceRetentionAnnotationsWhenTypealias.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typealiasToJavaWithAnnotationArgument.kt")
|
||||
public void testTypealiasToJavaWithAnnotationArgument() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/multiplatformCompositeAnalysis/annotationMatching/typealiasToJavaWithAnnotationArgument.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("typealiasedAnnotation.kt")
|
||||
public void testTypealiasedAnnotation() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user