FIR: Implement FirIdePredicateBasedProvider
This commit is contained in:
+223
@@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.analysis.low.level.api.fir.providers
|
||||||
|
|
||||||
|
import kotlinx.collections.immutable.PersistentList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getResolveState
|
||||||
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.resolveToFirSymbol
|
||||||
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.getContainingFile
|
||||||
|
import org.jetbrains.kotlin.analysis.providers.KotlinAnnotationsResolver
|
||||||
|
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider
|
||||||
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
|
import org.jetbrains.kotlin.fir.caches.FirCache
|
||||||
|
import org.jetbrains.kotlin.fir.caches.createCache
|
||||||
|
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||||
|
import org.jetbrains.kotlin.fir.caches.getValue
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
|
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.predicate.*
|
||||||
|
import org.jetbrains.kotlin.fir.extensions.registeredPluginAnnotations
|
||||||
|
import org.jetbrains.kotlin.fir.psi
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||||
|
import org.jetbrains.kotlin.fir.types.classId
|
||||||
|
import org.jetbrains.kotlin.fir.visitors.FirVisitor
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PSI index based implementation of [FirPredicateBasedProvider].
|
||||||
|
*/
|
||||||
|
internal class LLFirIdePredicateBasedProvider(
|
||||||
|
private val session: FirSession,
|
||||||
|
private val annotationsResolver: KotlinAnnotationsResolver,
|
||||||
|
private val declarationProvider: KotlinDeclarationProvider,
|
||||||
|
) : FirPredicateBasedProvider() {
|
||||||
|
|
||||||
|
private val registeredPluginAnnotations: FirRegisteredPluginAnnotations
|
||||||
|
get() = session.registeredPluginAnnotations
|
||||||
|
|
||||||
|
private val declarationOwnersCache: FirCache<FirFile, FirOwnersStorage, Nothing?> =
|
||||||
|
session.firCachesFactory.createCache { firFile -> FirOwnersStorage.collectOwners(firFile) }
|
||||||
|
|
||||||
|
override fun getSymbolsByPredicate(predicate: DeclarationPredicate): List<FirBasedSymbol<*>> {
|
||||||
|
val annotations = registeredPluginAnnotations.getAnnotationsForPredicate(predicate)
|
||||||
|
val annotatedDeclarations = annotations
|
||||||
|
.asSequence()
|
||||||
|
.flatMap { annotationsResolver.declarationsByAnnotation(ClassId.topLevel(it)) }
|
||||||
|
.toSet()
|
||||||
|
|
||||||
|
return annotatedDeclarations
|
||||||
|
.asSequence()
|
||||||
|
.mapNotNull { it.findFirDeclaration() }
|
||||||
|
.filter { matches(predicate, it) }
|
||||||
|
.map { it.symbol }
|
||||||
|
.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtElement.findFirDeclaration(): FirDeclaration? {
|
||||||
|
if (this !is KtDeclaration) return null
|
||||||
|
|
||||||
|
if (this !is KtClassLikeDeclaration &&
|
||||||
|
this !is KtNamedFunction &&
|
||||||
|
this !is KtConstructor<*> &&
|
||||||
|
this !is KtProperty
|
||||||
|
) return null
|
||||||
|
|
||||||
|
val resolveState = this.getResolveState()
|
||||||
|
return this.resolveToFirSymbol(resolveState).fir
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOwnersOfDeclaration(declaration: FirDeclaration): List<FirBasedSymbol<*>>? {
|
||||||
|
val firFile = declaration.getContainingFile() ?: return null
|
||||||
|
val declarationOwners = declarationOwnersCache.getValue(firFile)
|
||||||
|
|
||||||
|
return declarationOwners.getOwners(declaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fileHasPluginAnnotations(file: FirFile): Boolean {
|
||||||
|
val targetKtFile = file.psi as? KtFile ?: return false
|
||||||
|
val pluginAnnotations = registeredPluginAnnotations.annotations
|
||||||
|
|
||||||
|
return pluginAnnotations.any {
|
||||||
|
val annotationId = ClassId.topLevel(it)
|
||||||
|
val markedDeclarations = annotationsResolver.declarationsByAnnotation(annotationId)
|
||||||
|
|
||||||
|
targetKtFile in markedDeclarations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun matches(predicate: DeclarationPredicate, declaration: FirDeclaration): Boolean {
|
||||||
|
return predicate.accept(matcher, declaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val matcher: Matcher = Matcher()
|
||||||
|
|
||||||
|
private inner class Matcher : DeclarationPredicateVisitor<Boolean, FirDeclaration>() {
|
||||||
|
override fun visitPredicate(predicate: DeclarationPredicate, data: FirDeclaration): Boolean {
|
||||||
|
error("When overrides for all possible DeclarationPredicate subtypes are implemented, " +
|
||||||
|
"this method should never be called, but it was called with $predicate")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitAny(predicate: DeclarationPredicate.Any, data: FirDeclaration): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitAnd(predicate: DeclarationPredicate.And, data: FirDeclaration): Boolean {
|
||||||
|
return predicate.a.accept(this, data) && predicate.b.accept(this, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitOr(predicate: DeclarationPredicate.Or, data: FirDeclaration): Boolean {
|
||||||
|
return predicate.a.accept(this, data) || predicate.b.accept(this, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitAnnotatedWith(predicate: AnnotatedWith, data: FirDeclaration): Boolean {
|
||||||
|
return annotationsOnDeclaration(data).any { it in predicate.annotations }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitUnderAnnotatedWith(predicate: UnderAnnotatedWith, data: FirDeclaration): Boolean {
|
||||||
|
return annotationsOnOuterDeclarations(data).any { it in predicate.annotations }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitAnnotatedWithMeta(predicate: AnnotatedWithMeta, data: FirDeclaration): Boolean {
|
||||||
|
return metaAnnotationsOnDeclaration(data).any { it in predicate.metaAnnotations }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitUnderMetaAnnotated(predicate: UnderMetaAnnotated, data: FirDeclaration): Boolean {
|
||||||
|
return metaAnnotationsOnOuterDeclarations(data).any { it in predicate.metaAnnotations }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun annotationsOnDeclaration(declaration: FirDeclaration): Set<AnnotationFqn> {
|
||||||
|
if (declaration.annotations.isEmpty()) return emptySet()
|
||||||
|
|
||||||
|
val firResolvedAnnotations = declaration.annotations
|
||||||
|
.asSequence()
|
||||||
|
.mapNotNull { it.annotationTypeRef as? FirResolvedTypeRef }
|
||||||
|
.mapNotNull { it.type.classId }
|
||||||
|
.map { it.asSingleFqName() }
|
||||||
|
.toSet()
|
||||||
|
|
||||||
|
if (firResolvedAnnotations.isNotEmpty()) return firResolvedAnnotations
|
||||||
|
|
||||||
|
val psiDeclaration = declaration.psi as? KtAnnotated ?: return emptySet()
|
||||||
|
val psiAnnotations = annotationsResolver.annotationsOnDeclaration(psiDeclaration)
|
||||||
|
|
||||||
|
return psiAnnotations.map { it.asSingleFqName() }.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun metaAnnotationsOnDeclaration(declaration: FirDeclaration): Set<AnnotationFqn> {
|
||||||
|
val directAnnotations = annotationsOnDeclaration(declaration)
|
||||||
|
val metaAnnotations = directAnnotations
|
||||||
|
.asSequence()
|
||||||
|
.mapNotNull { declarationProvider.getClassesByClassId(ClassId.topLevel(it)).singleOrNull() }
|
||||||
|
.flatMap { annotationsResolver.annotationsOnDeclaration(it) }
|
||||||
|
.toSet()
|
||||||
|
|
||||||
|
return metaAnnotations.map { it.asSingleFqName() }.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun annotationsOnOuterDeclarations(declaration: FirDeclaration): Set<AnnotationFqn> {
|
||||||
|
return getOwnersOfDeclaration(declaration)?.flatMap { annotationsOnDeclaration(it.fir) }.orEmpty().toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun metaAnnotationsOnOuterDeclarations(declaration: FirDeclaration): Set<AnnotationFqn> {
|
||||||
|
return getOwnersOfDeclaration(declaration)?.flatMap { metaAnnotationsOnDeclaration(it.fir) }.orEmpty().toSet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FirOwnersStorage(private val declarationToOwner: Map<FirDeclaration, List<FirBasedSymbol<*>>>) {
|
||||||
|
fun getOwners(declaration: FirDeclaration): List<FirBasedSymbol<*>>? = declarationToOwner[declaration]
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun collectOwners(file: FirFile): FirOwnersStorage {
|
||||||
|
val declarationToOwners = hashMapOf<FirDeclaration, List<FirBasedSymbol<*>>>()
|
||||||
|
val psiToFir = hashMapOf<KtElement, FirDeclaration>()
|
||||||
|
|
||||||
|
file.forEachElementWithContainers { element, owners ->
|
||||||
|
if (element !is FirDeclaration) return@forEachElementWithContainers
|
||||||
|
|
||||||
|
declarationToOwners[element] = owners
|
||||||
|
|
||||||
|
val psiDeclaration = element.psi
|
||||||
|
if (psiDeclaration is KtElement) {
|
||||||
|
// FIXME we actually have a problem with KtFakeSourceElement sources
|
||||||
|
psiToFir.putIfAbsent(psiDeclaration, element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FirOwnersStorage(declarationToOwners)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Walks over every [FirElement] in [this] file and invokes [saveDeclaration] on it, passing each element and the list of its containing
|
||||||
|
* declarations (like file, classes, functions/properties and so on).
|
||||||
|
*/
|
||||||
|
private inline fun FirFile.forEachElementWithContainers(
|
||||||
|
crossinline saveDeclaration: (element: FirElement, owners: List<FirBasedSymbol<*>>) -> Unit
|
||||||
|
) {
|
||||||
|
val declarationsCollector = object : FirVisitor<Unit, PersistentList<FirBasedSymbol<*>>>() {
|
||||||
|
override fun visitElement(element: FirElement, data: PersistentList<FirBasedSymbol<*>>) {
|
||||||
|
if (element is FirDeclaration) {
|
||||||
|
saveDeclaration(element, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
element.acceptChildren(
|
||||||
|
visitor = this,
|
||||||
|
data = if (element is FirDeclaration) data.add(element.symbol) else data
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accept(declarationsCollector, persistentListOf())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user