[FIR] Properly handle nested annotations on compiler-required annotation phase
There was a case when we visited the same declaration multiple times because of forward-referencing and visiting with designation So because of this there were two changes required: - remove assertion about visiting the same declaration twice (it's fine since we don't actually resolve annotations twice) - not skipping resolution of class children if annotations on this class are already resolved ^KT-61388 Fixed KT-62854
This commit is contained in:
committed by
Space Team
parent
80257dc58f
commit
0325ffaa01
+6
@@ -767,6 +767,12 @@ public class DiagnosticCompilerTestFirTestdataTestGenerated extends AbstractDiag
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/annotations"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/annotations"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kindaCompilerRequiredNestedAnnotation.kt")
|
||||||
|
public void testKindaCompilerRequiredNestedAnnotation() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/annotations/kindaCompilerRequiredNestedAnnotation.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("kt43936.kt")
|
@TestMetadata("kt43936.kt")
|
||||||
public void testKt43936() throws Exception {
|
public void testKt43936() throws Exception {
|
||||||
|
|||||||
+6
@@ -767,6 +767,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFirTestDataTestGenerated
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/annotations"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/annotations"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kindaCompilerRequiredNestedAnnotation.kt")
|
||||||
|
public void testKindaCompilerRequiredNestedAnnotation() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/annotations/kindaCompilerRequiredNestedAnnotation.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("kt43936.kt")
|
@TestMetadata("kt43936.kt")
|
||||||
public void testKt43936() throws Exception {
|
public void testKt43936() throws Exception {
|
||||||
|
|||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
FILE: a.kt
|
||||||
|
@R|SinceKotlin|() public final class Some : R|kotlin/Any| {
|
||||||
|
public constructor(): R|Some| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
FILE: b.kt
|
||||||
|
@R|SinceKotlin.SinceKotlin|() public final class Other : R|kotlin/Any| {
|
||||||
|
public constructor(): R|Other| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
FILE: SinceKotlin.kt
|
||||||
|
public final annotation class SinceKotlin : R|kotlin/Annotation| {
|
||||||
|
public constructor(): R|SinceKotlin| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
public final annotation class SinceKotlin : R|kotlin/Annotation| {
|
||||||
|
public constructor(): R|SinceKotlin.SinceKotlin| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Vendored
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// ISSUE: KT-61388
|
||||||
|
// FILE: a.kt
|
||||||
|
@SinceKotlin
|
||||||
|
class Some
|
||||||
|
|
||||||
|
// FILE: b.kt
|
||||||
|
@SinceKotlin.SinceKotlin
|
||||||
|
class Other
|
||||||
|
|
||||||
|
// FILE: SinceKotlin.kt
|
||||||
|
annotation class SinceKotlin {
|
||||||
|
annotation class SinceKotlin
|
||||||
|
}
|
||||||
+6
@@ -767,6 +767,12 @@ public class FirLightTreeDiagnosticsTestGenerated extends AbstractFirLightTreeDi
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/annotations"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/annotations"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kindaCompilerRequiredNestedAnnotation.kt")
|
||||||
|
public void testKindaCompilerRequiredNestedAnnotation() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/annotations/kindaCompilerRequiredNestedAnnotation.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("kt43936.kt")
|
@TestMetadata("kt43936.kt")
|
||||||
public void testKt43936() throws Exception {
|
public void testKt43936() throws Exception {
|
||||||
|
|||||||
+6
@@ -767,6 +767,12 @@ public class FirPsiDiagnosticTestGenerated extends AbstractFirPsiDiagnosticTest
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/annotations"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/annotations"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kindaCompilerRequiredNestedAnnotation.kt")
|
||||||
|
public void testKindaCompilerRequiredNestedAnnotation() throws Exception {
|
||||||
|
runTest("compiler/fir/analysis-tests/testData/resolve/annotations/kindaCompilerRequiredNestedAnnotation.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("kt43936.kt")
|
@TestMetadata("kt43936.kt")
|
||||||
public void testKt43936() throws Exception {
|
public void testKt43936() throws Exception {
|
||||||
|
|||||||
+53
-4
@@ -31,10 +31,56 @@ import org.jetbrains.kotlin.fir.visitors.transformSingle
|
|||||||
import org.jetbrains.kotlin.fir.withFileAnalysisExceptionWrapping
|
import org.jetbrains.kotlin.fir.withFileAnalysisExceptionWrapping
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Little explanation the logic of this phase
|
||||||
|
*
|
||||||
|
* - this transformer visits all declarations and tries to resolve each annotation that looks like compiler-required by short name
|
||||||
|
* - if there are any meta-annotations registered by plugins, then each annotation is considered as "potentially compiler-required"
|
||||||
|
* - transformer resolves:
|
||||||
|
* - annotation types
|
||||||
|
* - annotation call arguments, if annotation classId mentioned in FirAnnotationsPlatformSpecificSupportComponent.requiredAnnotationsWithArguments
|
||||||
|
* - if annotation is considered as compiler-required after the resolution, the resolved type is saved in the annotation with
|
||||||
|
* FirAnnotationResolvePhase.CompilerRequiredAnnotations phase
|
||||||
|
* - if annotation potentially can be annotated with meta-annotation, the phase performs a designated jump to corresponding annotation class
|
||||||
|
* to resolve annotations on it. This jump resolves annotations only on last declaration from designation path
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* ```
|
||||||
|
* // FILE: plugin.kt
|
||||||
|
* annotation class MetaAnnotation
|
||||||
|
*
|
||||||
|
* // FILE: a.kt
|
||||||
|
* @A // (1)
|
||||||
|
* class Some
|
||||||
|
*
|
||||||
|
* // FILE: b.kt
|
||||||
|
* @A.B // (2)
|
||||||
|
* class Other
|
||||||
|
*
|
||||||
|
* // FILE: SinceKotlin.kt
|
||||||
|
* @MetaAnnotation // (3)
|
||||||
|
* annotation class A {
|
||||||
|
* @MetaAnnotation // (4)
|
||||||
|
* annotation class B
|
||||||
|
*
|
||||||
|
* @MetaAnnotation // (5)
|
||||||
|
* annotation class C
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* 1. visit class `Some`, resolve (1)
|
||||||
|
* 2. there are meta-annotations -> jump to class `A` and resolve (3)
|
||||||
|
* 3. visit class `Other`, resolved (2)
|
||||||
|
* 4. there are meta-annotations -> jump to class `A.B` and resolve (4)
|
||||||
|
* 5. visit class `A`, skip annotations on `A` since they are already resolved
|
||||||
|
* 6. visit class `A.B`, skip annotations on `A.B` since they are already resolved
|
||||||
|
* 7. visit class `A.C`, resolve (5)
|
||||||
|
*/
|
||||||
class FirCompilerRequiredAnnotationsResolveProcessor(
|
class FirCompilerRequiredAnnotationsResolveProcessor(
|
||||||
session: FirSession,
|
session: FirSession,
|
||||||
scopeSession: ScopeSession
|
scopeSession: ScopeSession
|
||||||
) : FirGlobalResolveProcessor(session, scopeSession, FirResolvePhase.COMPILER_REQUIRED_ANNOTATIONS) {
|
) : FirGlobalResolveProcessor(session, scopeSession, COMPILER_REQUIRED_ANNOTATIONS) {
|
||||||
|
|
||||||
override fun process(files: Collection<FirFile>) {
|
override fun process(files: Collection<FirFile>) {
|
||||||
val computationSession = CompilerRequiredAnnotationsComputationSession()
|
val computationSession = CompilerRequiredAnnotationsComputationSession()
|
||||||
@@ -150,9 +196,7 @@ open class CompilerRequiredAnnotationsComputationSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun recordThatAnnotationsAreResolved(declaration: FirAnnotationContainer) {
|
fun recordThatAnnotationsAreResolved(declaration: FirAnnotationContainer) {
|
||||||
if (!declarationsWithResolvedAnnotations.add(declaration)) {
|
declarationsWithResolvedAnnotations.add(declaration)
|
||||||
error("Annotations are resolved twice")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resolveAnnotationsOnAnnotationIfNeeded(symbol: FirRegularClassSymbol, scopeSession: ScopeSession) {
|
fun resolveAnnotationsOnAnnotationIfNeeded(symbol: FirRegularClassSymbol, scopeSession: ScopeSession) {
|
||||||
@@ -198,6 +242,11 @@ class FirSpecificAnnotationResolveTransformer(
|
|||||||
computationSession: CompilerRequiredAnnotationsComputationSession
|
computationSession: CompilerRequiredAnnotationsComputationSession
|
||||||
) : AbstractFirSpecificAnnotationResolveTransformer(session, scopeSession, computationSession) {
|
) : AbstractFirSpecificAnnotationResolveTransformer(session, scopeSession, computationSession) {
|
||||||
override fun shouldTransformDeclaration(declaration: FirDeclaration): Boolean {
|
override fun shouldTransformDeclaration(declaration: FirDeclaration): Boolean {
|
||||||
|
/*
|
||||||
|
* Even if annotations on class are resolved, annotations on nested declarations might be not resolved yet
|
||||||
|
* It may happen if we visited a top-level class with designated transformer with this class as target of designation
|
||||||
|
*/
|
||||||
|
if (declaration is FirRegularClass) return true
|
||||||
@OptIn(PrivateForInline::class)
|
@OptIn(PrivateForInline::class)
|
||||||
return !computationSession.annotationsAreResolved(declaration, treatNonSourceDeclarationsAsResolved = true)
|
return !computationSession.annotationsAreResolved(declaration, treatNonSourceDeclarationsAsResolved = true)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user