[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:
Dmitriy Novozhilov
2022-11-25 12:08:43 +02:00
committed by Space Team
parent 6a9b525ca0
commit 6864433581
48 changed files with 1065 additions and 387 deletions
@@ -967,6 +967,14 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.PLUGIN_ANNOTATION_AMBIGUITY) { firDiagnostic ->
PluginAnnotationAmbiguityImpl(
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b),
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.OPT_IN_USAGE) { firDiagnostic ->
OptInUsageImpl(
firDiagnostic.a,
@@ -697,6 +697,12 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = AnnotationInWhereClauseError::class
}
abstract class PluginAnnotationAmbiguity : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = PluginAnnotationAmbiguity::class
abstract val typeFromCompilerPhase: KtType
abstract val typeFromTypesPhase: KtType
}
abstract class OptInUsage : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = OptInUsage::class
abstract val optInMarkerFqName: FqName
@@ -837,6 +837,13 @@ internal class AnnotationInWhereClauseErrorImpl(
override val token: KtLifetimeToken,
) : KtFirDiagnostic.AnnotationInWhereClauseError(), KtAbstractFirDiagnostic<KtAnnotationEntry>
internal class PluginAnnotationAmbiguityImpl(
override val typeFromCompilerPhase: KtType,
override val typeFromTypesPhase: KtType,
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
) : KtFirDiagnostic.PluginAnnotationAmbiguity(), KtAbstractFirDiagnostic<PsiElement>
internal class OptInUsageImpl(
override val optInMarkerFqName: FqName,
override val message: String,
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.fir.SessionConfiguration
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmTypeMapper
import org.jetbrains.kotlin.fir.extensions.FirEmptyPredicateBasedProvider
import org.jetbrains.kotlin.fir.extensions.FirPredicateBasedProvider
import org.jetbrains.kotlin.fir.extensions.FirRegisteredPluginAnnotations
import org.jetbrains.kotlin.fir.resolve.providers.FirDependenciesSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirProvider
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
@@ -99,6 +100,7 @@ internal class LLFirNonUnderContentRootSessionFactory(private val project: Proje
register(FirDependenciesSymbolProvider::class, dependencyProvider)
register(FirDependenciesSymbolProvider::class, dependencyProvider)
register(FirJvmTypeMapper::class, FirJvmTypeMapper(this))
register(FirRegisteredPluginAnnotations::class, FirRegisteredPluginAnnotations.Empty)
}
}
@@ -22,20 +22,15 @@ import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.extensions.AnnotationFqn
import org.jetbrains.kotlin.fir.extensions.FirPredicateBasedProvider
import org.jetbrains.kotlin.fir.extensions.FirRegisteredPluginAnnotations
import org.jetbrains.kotlin.fir.extensions.*
import org.jetbrains.kotlin.fir.extensions.predicate.AbstractPredicate
import org.jetbrains.kotlin.fir.extensions.predicate.DeclarationPredicate
import org.jetbrains.kotlin.fir.extensions.predicate.LookupPredicate
import org.jetbrains.kotlin.fir.extensions.predicate.PredicateVisitor
import org.jetbrains.kotlin.fir.extensions.registeredPluginAnnotations
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.resolve.getCorrespondingClassSymbolOrNull
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
import org.jetbrains.kotlin.fir.types.classId
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.name.ClassId
@@ -138,23 +133,11 @@ internal class LLFirIdePredicateBasedProvider(
}
override fun visitMetaAnnotatedWith(predicate: AbstractPredicate.MetaAnnotatedWith<P>, data: FirDeclaration): Boolean {
val visited = mutableSetOf<FirRegularClassSymbol>()
return data.annotations.any { annotation ->
annotation.markedWithMetaAnnotation(predicate.metaAnnotations, visited)
annotation.markedWithMetaAnnotation(session, data, predicate.metaAnnotations)
}
}
private fun FirAnnotation.markedWithMetaAnnotation(
metaAnnotations: Set<AnnotationFqn>,
visited: MutableSet<FirRegularClassSymbol>
): Boolean {
val symbol = this.getCorrespondingClassSymbolOrNull(session) ?: return false
if (!visited.add(symbol)) return false
if (symbol.classId.asSingleFqName() in metaAnnotations) return true
if (symbol.resolvedAnnotationsWithClassIds.any { it.markedWithMetaAnnotation(metaAnnotations, visited) }) return true
return false
}
override fun visitParentAnnotatedWith(predicate: AbstractPredicate.ParentAnnotatedWith<P>, data: FirDeclaration): Boolean {
val parent = data.directParentDeclaration ?: return false
val parentPredicate = DeclarationPredicate.AnnotatedWith(predicate.annotations)
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.providers
import org.jetbrains.kotlin.analysis.providers.KotlinAnnotationsResolver
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.caches.*
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.extensions.AbstractFirRegisteredPluginAnnotations
import org.jetbrains.kotlin.fir.extensions.AnnotationFqn
import org.jetbrains.kotlin.name.ClassId
@@ -25,14 +26,25 @@ internal class LLFirIdeRegisteredPluginAnnotations(
get() = allAnnotationsCache.getValue()
private val allAnnotationsCache: FirLazyValue<Set<AnnotationFqn>, Nothing?> = session.firCachesFactory.createLazyValue {
// at this point, annotationsFromPlugins should be collected
annotationsFromPlugins
// at this point, both metaAnnotations and annotationsFromPlugins should be collected
val result = metaAnnotations.flatMapTo(mutableSetOf()) { getAnnotationsWithMetaAnnotation(it) }
if (result.isEmpty()) {
annotationsFromPlugins
} else {
result += annotationsFromPlugins
result
}
}
// MetaAnnotation -> Annotations
private val annotationsWithMetaAnnotationCache: FirCache<AnnotationFqn, Set<AnnotationFqn>, Nothing?> =
session.firCachesFactory.createCache { metaAnnotation -> collectAnnotationsWithMetaAnnotation(metaAnnotation) }
override fun getAnnotationsWithMetaAnnotation(metaAnnotation: AnnotationFqn): Collection<AnnotationFqn> {
return annotationsWithMetaAnnotationCache.getValue(metaAnnotation)
}
private fun collectAnnotationsWithMetaAnnotation(metaAnnotation: AnnotationFqn): Set<FqName> {
val annotatedDeclarations = annotationsResolver.declarationsByAnnotation(ClassId.topLevel(metaAnnotation))
@@ -47,4 +59,8 @@ internal class LLFirIdeRegisteredPluginAnnotations(
override fun saveAnnotationsFromPlugin(annotations: Collection<AnnotationFqn>) {
annotationsFromPlugins += annotations
}
override fun registerUserDefinedAnnotation(metaAnnotation: AnnotationFqn, annotationClasses: Collection<FirRegularClass>) {
error("This method should never be called in IDE mode")
}
}
@@ -12,13 +12,14 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkPhase
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.CompilerRequiredAnnotationsComputationSession
import org.jetbrains.kotlin.fir.resolve.transformers.plugin.FirCompilerRequiredAnnotationsResolveTransformer
internal class LLFirDesignatedAnnotationsResolveTransformed(
private val designation: FirDeclarationDesignationWithFile,
session: FirSession,
scopeSession: ScopeSession,
) : LLFirLazyTransformer, FirCompilerRequiredAnnotationsResolveTransformer(session, scopeSession) {
) : LLFirLazyTransformer, FirCompilerRequiredAnnotationsResolveTransformer(session, scopeSession, CompilerRequiredAnnotationsComputationSession()) {
private fun moveNextDeclaration(designationIterator: Iterator<FirDeclaration>) {
if (!designationIterator.hasNext()) {