org.jetbrains.kotlin.fir.extensions.FirPredicateBasedProviderImpl.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2020 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.fir.extensions
import com.google.common.collect.LinkedHashMultimap
import com.google.common.collect.Multimap
import kotlinx.collections.immutable.PersistentList
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.NoMutableState
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
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.resolve.fqName
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.fir.types.toRegularClassSymbol
@NoMutableState
class FirPredicateBasedProviderImpl(private val session: FirSession) : FirPredicateBasedProvider() {
private val registeredPluginAnnotations = session.registeredPluginAnnotations
private val cache = Cache()
override fun getSymbolsByPredicate(predicate: LookupPredicate): List> {
val annotations = predicate.annotations
if (annotations.isEmpty()) return emptyList()
val declarations = annotations.flatMapTo(mutableSetOf()) {
cache.declarationByAnnotation[it] + cache.declarationsUnderAnnotated[it]
}
return declarations.filter { matches(predicate, it) }.map { it.symbol }
}
override fun fileHasPluginAnnotations(file: FirFile): Boolean {
return file in cache.filesWithPluginAnnotations
}
@FirExtensionApiInternals
override fun registerAnnotatedDeclaration(declaration: FirDeclaration, owners: PersistentList) {
cache.ownersForDeclaration[declaration] = owners
registerOwnersDeclarations(declaration, owners)
if (declaration.annotations.isEmpty()) return
val matchingAnnotations = declaration.annotations
.mapNotNull { it.fqName(session) }
.filter { it in registeredPluginAnnotations.annotations }
.takeIf { it.isNotEmpty() }
?: return
owners.lastOrNull()?.let { owner ->
matchingAnnotations.forEach { cache.declarationsHasAnnotated.put(it, owner) }
cache.annotationsOfHasAnnotated.putAll(owner, matchingAnnotations)
}
matchingAnnotations.forEach { cache.declarationByAnnotation.put(it, declaration) }
cache.annotationsOfDeclaration.putAll(declaration, matchingAnnotations)
val file = owners.first() as FirFile
cache.filesWithPluginAnnotations += file
}
override fun getOwnersOfDeclaration(declaration: FirDeclaration): List>? {
return cache.ownersForDeclaration[declaration]?.map { it.symbol }
}
private fun registerOwnersDeclarations(declaration: FirDeclaration, owners: PersistentList) {
val lastOwner = owners.lastOrNull() ?: return
val annotationsFromLastOwner = cache.annotationsOfDeclaration[lastOwner]
val annotationsFromPreviousOwners = cache.annotationsOfUnderAnnotated[lastOwner]
annotationsFromLastOwner.forEach { cache.declarationsParentAnnotated.put(it, declaration) }
cache.annotationsOfParentAnnotated.putAll(declaration, annotationsFromLastOwner)
val allParentDeclarations = annotationsFromLastOwner + annotationsFromPreviousOwners
allParentDeclarations.forEach { cache.declarationsUnderAnnotated.put(it, declaration) }
cache.annotationsOfUnderAnnotated.putAll(declaration, allParentDeclarations)
}
// ---------------------------------- Matching ----------------------------------
override fun matches(predicate: AbstractPredicate<*>, declaration: FirDeclaration): Boolean {
/*
* If declaration came from the other source session we should delegate to provider from
* that session, because it stores all caches about its own declarations
*/
val declarationSession = declaration.moduleData.session
if (declarationSession.kind == FirSession.Kind.Source && declarationSession !== session) {
return declarationSession.predicateBasedProvider.matches(predicate, declaration)
}
return when (predicate) {
is DeclarationPredicate -> predicate.accept(declarationPredicateMatcher, declaration)
is LookupPredicate -> predicate.accept(lookupPredicateMatcher, declaration)
}
}
private val declarationPredicateMatcher = Matcher()
private val lookupPredicateMatcher = Matcher()
private inner class Matcher> : PredicateVisitor
() {
override fun visitPredicate(predicate: AbstractPredicate
, data: FirDeclaration): Boolean {
throw IllegalStateException("Should not be there")
}
override fun visitAnd(predicate: AbstractPredicate.And
, data: FirDeclaration): Boolean {
return predicate.a.accept(this, data) && predicate.b.accept(this, data)
}
override fun visitOr(predicate: AbstractPredicate.Or
, data: FirDeclaration): Boolean {
return predicate.a.accept(this, data) || predicate.b.accept(this, data)
}
// ------------------------------------ Annotated ------------------------------------
override fun visitAnnotatedWith(predicate: AbstractPredicate.AnnotatedWith
, data: FirDeclaration): Boolean {
return matchWith(data, predicate.annotations)
}
override fun visitAncestorAnnotatedWith(
predicate: AbstractPredicate.AncestorAnnotatedWith
,
data: FirDeclaration
): Boolean {
return matchUnder(data, predicate.annotations)
}
override fun visitParentAnnotatedWith(
predicate: AbstractPredicate.ParentAnnotatedWith
,
data: FirDeclaration
): Boolean {
return matchParentWith(data, predicate.annotations)
}
override fun visitHasAnnotatedWith(predicate: AbstractPredicate.HasAnnotatedWith
, data: FirDeclaration): Boolean {
return matchHasAnnotatedWith(data, predicate.annotations)
}
// ------------------------------------ Meta-annotated ------------------------------------
override fun visitMetaAnnotatedWith(predicate: AbstractPredicate.MetaAnnotatedWith
, data: FirDeclaration): Boolean {
return data.annotations.any { annotation ->
annotation.markedWithMetaAnnotation(session, data, predicate.metaAnnotations, predicate.includeItself)
}
}
// ------------------------------------ Utilities ------------------------------------
private fun matchWith(declaration: FirDeclaration, annotations: Set): Boolean {
return when (declaration.origin) {
FirDeclarationOrigin.Library, is FirDeclarationOrigin.Java -> matchNonIndexedDeclaration(declaration, annotations)
else -> when (declaration is FirClass && declaration.isLocal) {
true -> matchNonIndexedDeclaration(declaration, annotations)
false -> cache.annotationsOfDeclaration[declaration].any { it in annotations }
}
}
}
private fun matchNonIndexedDeclaration(declaration: FirDeclaration, annotations: Set): Boolean {
return declaration.annotations.any { it.fqName(session) in annotations }
}
private fun matchUnder(declaration: FirDeclaration, annotations: Set): Boolean {
return cache.annotationsOfUnderAnnotated[declaration].any { it in annotations }
}
private fun matchParentWith(declaration: FirDeclaration, annotations: Set): Boolean {
return cache.annotationsOfParentAnnotated[declaration].any { it in annotations }
}
private fun matchHasAnnotatedWith(declaration: FirDeclaration, annotations: Set): Boolean {
return cache.annotationsOfHasAnnotated[declaration].any { it in annotations }
}
}
// ---------------------------------- Cache ----------------------------------
private class Cache {
val declarationByAnnotation: Multimap = LinkedHashMultimap.create()
val annotationsOfDeclaration: LinkedHashMultimap = LinkedHashMultimap.create()
val declarationsUnderAnnotated: Multimap = LinkedHashMultimap.create()
val annotationsOfUnderAnnotated: LinkedHashMultimap = LinkedHashMultimap.create()
val declarationsParentAnnotated: Multimap = LinkedHashMultimap.create()
val annotationsOfParentAnnotated: Multimap = LinkedHashMultimap.create()
val declarationsHasAnnotated: Multimap = LinkedHashMultimap.create()
val annotationsOfHasAnnotated: Multimap = LinkedHashMultimap.create()
val ownersForDeclaration: MutableMap> = mutableMapOf()
val filesWithPluginAnnotations: MutableSet = mutableSetOf()
}
}
fun FirAnnotation.markedWithMetaAnnotation(
session: FirSession,
containingDeclaration: FirDeclaration,
metaAnnotations: Set,
includeItself: Boolean
): Boolean {
containingDeclaration.symbol.lazyResolveToPhase(FirResolvePhase.COMPILER_REQUIRED_ANNOTATIONS)
return annotationTypeRef.coneTypeSafe()
?.toRegularClassSymbol(session)
.markedWithMetaAnnotationImpl(session, metaAnnotations, includeItself, mutableSetOf())
}
fun FirRegularClassSymbol?.markedWithMetaAnnotationImpl(
session: FirSession,
metaAnnotations: Set,
includeItself: Boolean,
visited: MutableSet,
resolvedCompilerAnnotations: (FirRegularClassSymbol) -> List = FirBasedSymbol<*>::resolvedCompilerAnnotationsWithClassIds,
): Boolean {
if (this == null) return false
if (!visited.add(this)) return false
if (this.classId.asSingleFqName() in metaAnnotations) return includeItself
return resolvedCompilerAnnotations(this)
.mapNotNull { it.annotationTypeRef.coneTypeSafe()?.toRegularClassSymbol(session) }
.any { it.markedWithMetaAnnotationImpl(session, metaAnnotations, includeItself = true, visited, resolvedCompilerAnnotations) }
}