[FIR] Support resolution of meta-annotations before supertypes
Now annotations are resolved with following algorithm:
1. On COMPILER_REQUIRED_ANNOTATIONS we resolve all annotations
and store results if this is compiler annotation, plugin annotation,
or annotation with meta-annotation (meta annotations are checked
recursively with designated resolution if needed)
2. On TYPES stage we resolve all those annotations once again and if
some annotation changes resolution then we keep type from p.1 and
report error on this annotation, so user should disambiguate it
Ambiguity may occur because of nested annotations with same name as
plugin annotations:
```
annotation class SomeAnnotation // (1) plugin annotation
open class Base {
annotation class SomeAnnotation // (2)
}
class Derived : Base() {
@SomeAnnotation // <-----------------
class Inner
}
```
At COMPILER_REQUIRED_ANNOTATIONS annotation call will be resolved to (1)
because at this stage supertypes are not resolved yet, and we consider
only importing scopes. At the TYPES stage we will find correct
annotation from supertype
This commit is contained in:
committed by
Space Team
parent
6a9b525ca0
commit
6864433581
+2
@@ -31,3 +31,5 @@ enum class Visibility {
|
||||
}
|
||||
|
||||
annotation class SupertypeWithTypeArgument(val kClass: KClass<*>)
|
||||
|
||||
annotation class MetaSupertype
|
||||
|
||||
+19
-3
@@ -5,8 +5,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.plugin
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.classId
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
|
||||
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
|
||||
import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension
|
||||
import org.jetbrains.kotlin.fir.extensions.predicate.DeclarationPredicate
|
||||
@@ -20,13 +25,14 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
/*
|
||||
* Adds MyInterface supertype for all classes annotated with @MyInterfaceSupertype
|
||||
* Adds MyInterface supertype for all classes annotated with @MyInterfaceSupertype or meta-annotated with MetaSupertype
|
||||
*/
|
||||
class SomeAdditionalSupertypeGenerator(session: FirSession) : FirSupertypeGenerationExtension(session) {
|
||||
companion object {
|
||||
private val myInterfaceClassId = ClassId(FqName("foo"), Name.identifier("MyInterface"))
|
||||
private val PREDICATE = DeclarationPredicate.create { annotated("MyInterfaceSupertype".fqn()) }
|
||||
|
||||
private val PREDICATE = DeclarationPredicate.create {
|
||||
annotated("MyInterfaceSupertype".fqn()) or metaAnnotated("MetaSupertype".fqn())
|
||||
}
|
||||
}
|
||||
|
||||
context(TypeResolveServiceContainer)
|
||||
@@ -35,6 +41,16 @@ class SomeAdditionalSupertypeGenerator(session: FirSession) : FirSupertypeGenera
|
||||
classLikeDeclaration: FirClassLikeDeclaration,
|
||||
resolvedSupertypes: List<FirResolvedTypeRef>
|
||||
): List<FirResolvedTypeRef> {
|
||||
if (classLikeDeclaration !is FirRegularClass) return emptyList()
|
||||
when (classLikeDeclaration.classKind) {
|
||||
ClassKind.CLASS,
|
||||
ClassKind.INTERFACE,
|
||||
ClassKind.OBJECT-> {}
|
||||
|
||||
ClassKind.ENUM_CLASS,
|
||||
ClassKind.ENUM_ENTRY,
|
||||
ClassKind.ANNOTATION_CLASS -> return emptyList()
|
||||
}
|
||||
if (resolvedSupertypes.any { it.type.classId == myInterfaceClassId }) return emptyList()
|
||||
return listOf(
|
||||
buildResolvedTypeRef {
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
Module: lib
|
||||
FILE: module_lib_metaAnnotationFromLibrary.kt
|
||||
@R|org/jetbrains/kotlin/fir/plugin/AllOpen|() public open annotation class Open1 : R|kotlin/Annotation| {
|
||||
public constructor(): R|Open1| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
@R|Open1|() public open annotation class Open2 : R|kotlin/Annotation| {
|
||||
public constructor(): R|Open2| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
@R|Open2|() public open annotation class Open3 : R|kotlin/Annotation| {
|
||||
public constructor(): R|Open3| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
Module: main
|
||||
FILE: module_main_metaAnnotationFromLibrary.kt
|
||||
@R|org/jetbrains/kotlin/fir/plugin/AllOpen|() public open class Zero : R|kotlin/Any| {
|
||||
public constructor(): R|Zero| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
@R|Open1|() public open class First : R|kotlin/Any| {
|
||||
public constructor(): R|First| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
@R|Open2|() public open class Second : R|kotlin/Any| {
|
||||
public constructor(): R|Second| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
@R|Open3|() public open class Third : R|kotlin/Any| {
|
||||
public constructor(): R|Third| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public final fun box(): R|kotlin/String| {
|
||||
lval a: R|<anonymous>| = object : R|Zero| {
|
||||
private constructor(): R|<anonymous>| {
|
||||
super<R|Zero|>()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lval b: R|<anonymous>| = object : R|First| {
|
||||
private constructor(): R|<anonymous>| {
|
||||
super<R|First|>()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lval c: R|<anonymous>| = object : R|Second| {
|
||||
private constructor(): R|<anonymous>| {
|
||||
super<R|Second|>()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lval d: R|<anonymous>| = object : R|Third| {
|
||||
private constructor(): R|<anonymous>| {
|
||||
super<R|Third|>()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
^box String(OK)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// MODULE: lib
|
||||
import org.jetbrains.kotlin.fir.plugin.AllOpen
|
||||
|
||||
@AllOpen
|
||||
annotation class Open1
|
||||
|
||||
@Open1
|
||||
annotation class Open2
|
||||
|
||||
@Open2
|
||||
annotation class Open3
|
||||
|
||||
// MODULE: main(lib)
|
||||
|
||||
import org.jetbrains.kotlin.fir.plugin.AllOpen
|
||||
|
||||
@AllOpen
|
||||
class Zero
|
||||
|
||||
@Open1
|
||||
class First
|
||||
|
||||
@Open2
|
||||
class Second
|
||||
|
||||
@Open3
|
||||
class Third
|
||||
|
||||
fun box(): String {
|
||||
val a = object : Zero() {}
|
||||
val b = object : First() {}
|
||||
val c = object : Second() {}
|
||||
val d = object : Third() {}
|
||||
return "OK"
|
||||
}
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
FILE: metaAnnotationClashesWithSupertype.kt
|
||||
@R|org/jetbrains/kotlin/fir/plugin/AllOpen|() public open annotation class Open : R|kotlin/Annotation| {
|
||||
public constructor(): R|Open| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public final annotation class Ann : R|kotlin/Annotation| {
|
||||
public constructor(): R|Ann| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public open class Base : R|kotlin/Any| {
|
||||
public constructor(): R|Base| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final annotation class Open : R|kotlin/Annotation| {
|
||||
public constructor(): R|Base.Open| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public final annotation class Ann : R|kotlin/Annotation| {
|
||||
public constructor(): R|Base.Ann| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public final class Derived : R|Base| {
|
||||
public constructor(): R|Derived| {
|
||||
super<R|Base|>()
|
||||
}
|
||||
|
||||
@<ERROR TYPE REF:
|
||||
Annotation type resolved differently on compiler annotation and types stages:
|
||||
- compiler annotations: Open
|
||||
- types stage: Base.Open
|
||||
>() @R|Base.Ann|() public open class ShouldBeFinal : R|kotlin/Any| {
|
||||
public constructor(): R|Derived.ShouldBeFinal| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public final class ShouldBeAnError : R|Derived.ShouldBeFinal| {
|
||||
public constructor(): R|ShouldBeAnError| {
|
||||
super<R|Derived.ShouldBeFinal|>()
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
import org.jetbrains.kotlin.fir.plugin.AllOpen
|
||||
|
||||
@AllOpen
|
||||
annotation class Open
|
||||
|
||||
annotation class Ann
|
||||
|
||||
open class Base {
|
||||
annotation class Open
|
||||
annotation class Ann
|
||||
}
|
||||
|
||||
class Derived : Base() {
|
||||
@<!PLUGIN_ANNOTATION_AMBIGUITY!>Open<!> // should be an error
|
||||
@Ann // should be ok
|
||||
class ShouldBeFinal
|
||||
}
|
||||
|
||||
class ShouldBeAnError : Derived.ShouldBeFinal()
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
FILE: main.kt
|
||||
package foo
|
||||
|
||||
public abstract interface MyInterface : R|kotlin/Any| {
|
||||
public open fun foo(): R|kotlin/Unit| {
|
||||
}
|
||||
|
||||
}
|
||||
@R|AddSupertype2|() public final class Second : R|kotlin/Any|, R|foo/MyInterface| {
|
||||
public constructor(): R|foo/Second| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
@R|AddSupertype3|() public final class Third : R|kotlin/Any|, R|foo/MyInterface| {
|
||||
public constructor(): R|foo/Third| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
@R|AddSupertype1|() public final class First : R|kotlin/Any|, R|foo/MyInterface| {
|
||||
public constructor(): R|foo/First| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
@R|org/jetbrains/kotlin/fir/plugin/MetaSupertype|() public final class Zero : R|kotlin/Any|, R|foo/MyInterface| {
|
||||
public constructor(): R|foo/Zero| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public final fun test(a: R|foo/Zero|, b: R|foo/First|, c: R|foo/Second|, d: R|foo/Third|): R|kotlin/Unit| {
|
||||
R|<local>/a|.R|foo/MyInterface.foo|()
|
||||
R|<local>/b|.R|foo/MyInterface.foo|()
|
||||
R|<local>/c|.R|foo/MyInterface.foo|()
|
||||
R|<local>/d|.R|foo/MyInterface.foo|()
|
||||
}
|
||||
FILE: ann3.kt
|
||||
@R|AddSupertype2|() public final annotation class AddSupertype3 : R|kotlin/Annotation| {
|
||||
public constructor(): R|AddSupertype3| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
FILE: ann2.kt
|
||||
@R|AddSupertype1|() public final annotation class AddSupertype2 : R|kotlin/Annotation| {
|
||||
public constructor(): R|AddSupertype2| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
FILE: ann1.kt
|
||||
@R|org/jetbrains/kotlin/fir/plugin/MetaSupertype|() public final annotation class AddSupertype1 : R|kotlin/Annotation| {
|
||||
public constructor(): R|AddSupertype1| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// FILE: main.kt
|
||||
package foo
|
||||
|
||||
import org.jetbrains.kotlin.fir.plugin.MetaSupertype
|
||||
|
||||
interface MyInterface {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
@AddSupertype2
|
||||
class Second
|
||||
|
||||
@AddSupertype3
|
||||
class Third
|
||||
|
||||
@AddSupertype1
|
||||
class First
|
||||
|
||||
@MetaSupertype
|
||||
class Zero
|
||||
|
||||
fun test(a: Zero, b: First, c: Second, d: Third) {
|
||||
a.foo()
|
||||
b.foo()
|
||||
c.foo()
|
||||
d.foo()
|
||||
}
|
||||
|
||||
// FILE: ann3.kt
|
||||
@AddSupertype2
|
||||
annotation class AddSupertype3
|
||||
|
||||
// FILE: ann2.kt
|
||||
@AddSupertype1
|
||||
annotation class AddSupertype2
|
||||
|
||||
// FILE: ann1.kt
|
||||
import org.jetbrains.kotlin.fir.plugin.MetaSupertype
|
||||
|
||||
@MetaSupertype
|
||||
annotation class AddSupertype1
|
||||
+6
@@ -43,6 +43,12 @@ public class FirPluginBlackBoxCodegenTestGenerated extends AbstractFirPluginBlac
|
||||
runTest("plugins/fir-plugin-prototype/testData/box/generatedClassWithMembersAndNestedClasses.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("metaAnnotationFromLibrary.kt")
|
||||
public void testMetaAnnotationFromLibrary() throws Exception {
|
||||
runTest("plugins/fir-plugin-prototype/testData/box/metaAnnotationFromLibrary.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("newSupertype.kt")
|
||||
public void testNewSupertype() throws Exception {
|
||||
|
||||
+12
@@ -111,6 +111,12 @@ public class FirPluginDiagnosticTestGenerated extends AbstractFirPluginDiagnosti
|
||||
runTest("plugins/fir-plugin-prototype/testData/diagnostics/status/metaAnnotation.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("metaAnnotationClashesWithSupertype.kt")
|
||||
public void testMetaAnnotationClashesWithSupertype() throws Exception {
|
||||
runTest("plugins/fir-plugin-prototype/testData/diagnostics/status/metaAnnotationClashesWithSupertype.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simpleAnnotation.kt")
|
||||
public void testSimpleAnnotation() throws Exception {
|
||||
@@ -133,6 +139,12 @@ public class FirPluginDiagnosticTestGenerated extends AbstractFirPluginDiagnosti
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/fir-plugin-prototype/testData/diagnostics/supertypes"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("metaAnnotationOrder.kt")
|
||||
public void testMetaAnnotationOrder() throws Exception {
|
||||
runTest("plugins/fir-plugin-prototype/testData/diagnostics/supertypes/metaAnnotationOrder.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
// WITH_STDLIB
|
||||
// SKIP_TXT
|
||||
|
||||
import kotlinx.serialization.*
|
||||
|
||||
@MetaSerializable
|
||||
annotation class TopLevel
|
||||
|
||||
class MetaSerializableNestedTest {
|
||||
<!META_SERIALIZABLE_NOT_APPLICABLE!>@MetaSerializable<!>
|
||||
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
|
||||
annotation class JsonComment(val comment: String)
|
||||
|
||||
object Nested2 {
|
||||
<!META_SERIALIZABLE_NOT_APPLICABLE!>@MetaSerializable<!>
|
||||
annotation class Nested3
|
||||
}
|
||||
|
||||
@<!UNRESOLVED_REFERENCE!>JsonComment<!>("class_comment")
|
||||
data class IntDataCommented(val i: Int)
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// SKIP_TXT
|
||||
|
||||
|
||||
Reference in New Issue
Block a user