diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirPredicateBasedProviderImpl.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirPredicateBasedProviderImpl.kt index 5c080182d9d..b03937ef8b1 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirPredicateBasedProviderImpl.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/extensions/FirPredicateBasedProviderImpl.kt @@ -44,6 +44,7 @@ class FirPredicateBasedProviderImpl(private val session: FirSession) : FirPredic return file in cache.filesWithPluginAnnotations } + @FirExtensionApiInternals override fun registerAnnotatedDeclaration(declaration: FirDeclaration, owners: PersistentList) { cache.ownersForDeclaration[declaration] = owners registerOwnersDeclarations(declaration, owners) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/AbstractFirSpecificAnnotationResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/AbstractFirSpecificAnnotationResolveTransformer.kt index 09a77db7f7c..b8bd38a6cc8 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/AbstractFirSpecificAnnotationResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/plugin/AbstractFirSpecificAnnotationResolveTransformer.kt @@ -17,10 +17,7 @@ import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.FirAnnotationResolvePhase import org.jetbrains.kotlin.fir.expressions.FirStatement -import org.jetbrains.kotlin.fir.extensions.AnnotationFqn -import org.jetbrains.kotlin.fir.extensions.markedWithMetaAnnotationImpl -import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider -import org.jetbrains.kotlin.fir.extensions.registeredPluginAnnotations +import org.jetbrains.kotlin.fir.extensions.* import org.jetbrains.kotlin.fir.resolve.ResolutionMode import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.transformers.DesignationState @@ -157,6 +154,7 @@ internal abstract class AbstractFirSpecificAnnotationResolveTransformer( } as FirTypeAlias } + @OptIn(FirExtensionApiInternals::class) override fun transformDeclaration(declaration: FirDeclaration, data: Nothing?): FirDeclaration { return (transformAnnotationContainer(declaration, data) as FirDeclaration).also { predicateBasedProvider.registerAnnotatedDeclaration(declaration, owners) diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/FirPredicateBasedProvider.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/FirPredicateBasedProvider.kt index 0100ff8969b..895c1ab8c1f 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/FirPredicateBasedProvider.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/FirPredicateBasedProvider.kt @@ -12,33 +12,56 @@ import org.jetbrains.kotlin.fir.NoMutableState import org.jetbrains.kotlin.fir.declarations.FirDeclaration import org.jetbrains.kotlin.fir.declarations.FirFile 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.symbols.FirBasedSymbol abstract class FirPredicateBasedProvider : FirSessionComponent { + /** + * @return list of all declarations from compiled source module which are matched to [predicate] + */ abstract fun getSymbolsByPredicate(predicate: LookupPredicate): List> + + /** + * @return list of all parents of [declaration] + */ abstract fun getOwnersOfDeclaration(declaration: FirDeclaration): List>? /** - * @return `true` iff file has a top-level annotation from the [FirRegisteredPluginAnnotations.annotations] list. + * @return `true` if file has a top-level annotation from the [FirRegisteredPluginAnnotations.annotations] list. * @see FirRegisteredPluginAnnotations.annotations */ abstract fun fileHasPluginAnnotations(file: FirFile): Boolean + + /** + * @return if [declaration] matches [predicate] or not + */ abstract fun matches(predicate: AbstractPredicate<*>, declaration: FirDeclaration): Boolean + /** + * @return if [declaration] matches [predicate] or not + */ fun matches(predicate: AbstractPredicate<*>, declaration: FirBasedSymbol<*>): Boolean { return matches(predicate, declaration.fir) } + /** + * @return if [declaration] matches any predicate from [predicates] or not + */ fun matches(predicates: List>, declaration: FirDeclaration): Boolean { return predicates.any { matches(it, declaration) } } + /** + * @return if [declaration] matches any predicate from [predicates] or not + */ fun matches(predicates: List>, declaration: FirBasedSymbol<*>): Boolean { return matches(predicates, declaration.fir) } + /** + * Utility method which should not be used from plugins + */ + @FirExtensionApiInternals open fun registerAnnotatedDeclaration(declaration: FirDeclaration, owners: PersistentList) {} } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/predicate/AbstractPredicate.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/predicate/AbstractPredicate.kt index 8b0507d4029..add10e3d263 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/predicate/AbstractPredicate.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/predicate/AbstractPredicate.kt @@ -6,13 +6,31 @@ package org.jetbrains.kotlin.fir.extensions.predicate import org.jetbrains.kotlin.fir.extensions.AnnotationFqn +import org.jetbrains.kotlin.fir.extensions.FirPredicateBasedProvider +/** + * Predicates are the mechanism for compiler plugins which allows to search for annotated declarations + * or check if some declaration matches some predicate or not. + * There are two different kinds of predicates: [LookupPredicate] and [DeclarationPredicate] + * + * [LookupPredicate] allows user to get all declarations in current module, which matches this predicate + * Both [LookupPredicate] and [DeclarationPredicate] allow user to check if some declaration matches the predicate + * The main component which can be used with predicate is [FirPredicateBasedProvider] + * + * The main difference between [LookupPredicate] and [DeclarationPredicate] is that [DeclarationPredicate] allows + * to create predicate with meta annotations, and [LookupPredicate] allows to use only annotations with predefined + * qualified names + */ sealed interface AbstractPredicate

> { val annotations: Set val metaAnnotations: Set fun accept(visitor: PredicateVisitor, data: D): R + /** + * Boolean combinator OR for two predicates, matches declaration if + * [a] matches declaration or [b] matches declaration + */ sealed interface Or

> : AbstractPredicate

{ val a: P val b: P @@ -22,6 +40,10 @@ sealed interface AbstractPredicate

> { } } + /** + * Boolean combinator AND for two predicates, matches declaration if + * [a] matches declaration and [b] matches declaration + */ sealed interface And

> : AbstractPredicate

{ val a: P val b: P @@ -33,31 +55,110 @@ sealed interface AbstractPredicate

> { // ------------------------------------ Annotated ------------------------------------ + /** + * Base class for all predicates with specific annotations + * Declaration will be matched if at least one of [annotations] is found + */ sealed interface Annotated

> : AbstractPredicate

{ override fun accept(visitor: PredicateVisitor, data: D): R { return visitor.visitAnnotated(this, data) } } + /** + * Matches declarations, which are annotated with [annotations] + * + * @Ann + * fun foo() {} + * + * fun bar(@Ann param: Int) {} + * + * @Ann + * class A { + * fun baz() {} + * + * class Nested { + * fun foobar() {} + * } + * } + * + * Matched symbols: [fun foo, parameter `param` from fun bar, class A] + */ sealed interface AnnotatedWith

> : Annotated

{ override fun accept(visitor: PredicateVisitor, data: D): R { return visitor.visitAnnotatedWith(this, data) } } + /** + * Matches declaration, if one of its containers annotated with [annotations] + * + * @Ann + * fun foo() {} + * + * fun bar(@Ann param: Int) {} + * + * @Ann + * class A { + * fun baz() {} + * + * class Nested { + * fun foobar() {} + * } + * } + * + * Matched symbols: [fun A.baz, class Nested, fun Nested.foobar] + */ sealed interface AncestorAnnotatedWith

> : Annotated

{ override fun accept(visitor: PredicateVisitor, data: D): R { return visitor.visitAncestorAnnotatedWith(this, data) } } - + /** + * Matches declaration, if its direct container annotated with [annotations] + * + * @Ann + * fun foo() {} + * + * fun bar(@Ann param: Int) {} + * + * @Ann + * class A { + * fun baz() {} + * + * class Nested { + * fun foobar() {} + * } + * } + * + * Matched symbols: [fun A.baz, class Nested] + */ sealed interface ParentAnnotatedWith

> : Annotated

{ override fun accept(visitor: PredicateVisitor, data: D): R { return visitor.visitParentAnnotatedWith(this, data) } } + /** + * Matches declaration, if one of its direct child declarations annotated with [annotations] + * + * @Ann + * fun foo() {} + * + * fun bar(@Ann param: Int) {} + * + * class A { + * @Ann + * fun baz() {} + * + * class Nested { + * fun foobar() {} + * } + * } + * + * Matched symbols: [fun bar, class A] + */ sealed interface HasAnnotatedWith

> : Annotated

{ override fun accept(visitor: PredicateVisitor, data: D): R { return visitor.visitHasAnnotatedWith(this, data) @@ -66,6 +167,38 @@ sealed interface AbstractPredicate

> { // ------------------------------------ MetaAnnotated ------------------------------------ + /** + * Matches declarations, which are annotated with annotations which are annotated with [metaAnnotations] + * Relation "annotation with meta annotation" is transitive. E.g. in snippet below some declaration will + * be matched with predicate MetaAnnotatedWith("Ann") if it is annotated with `@Ann`, `@Some` or `@Other` + * + * @Ann + * annotation class Some + * + * @Some + * annotation class Other + * + * + * @Some + * fun foo() {} + * + * fun bar(@Ann param: Int) {} + * + * @Some + * class A { + * fun baz() {} + * + * class Nested { + * @Other + * fun foobar() {} + * } + * } + * + * Matched symbols: [fun foo, class A, fun A.Nested.foobar] + * + * Note that [MetaAnnotatedWith] predicate has no implementation in [LookupPredicate] hierarchy + * and can not be used for global lookup + */ sealed interface MetaAnnotatedWith

> : AbstractPredicate

{ override fun accept(visitor: PredicateVisitor, data: D): R { return visitor.visitMetaAnnotatedWith(this, data) diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/predicate/DeclarationPredicate.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/predicate/DeclarationPredicate.kt index 03af7461545..5871e39492f 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/predicate/DeclarationPredicate.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/predicate/DeclarationPredicate.kt @@ -9,7 +9,10 @@ import org.jetbrains.kotlin.fir.extensions.AnnotationFqn // -------------------------------------------- Predicates -------------------------------------------- -// todo: Missing KDOC +/** + * For reference read KDoc to [AbstractPredicate] + * @see [AbstractPredicate] + */ sealed class DeclarationPredicate : AbstractPredicate { abstract override val annotations: Set abstract override val metaAnnotations: Set @@ -42,10 +45,6 @@ sealed class DeclarationPredicate : AbstractPredicate { // ------------------------------------ Annotated ------------------------------------ - /** - * Base class for all predicates with specific annotations - * Declaration will be matched if at least one of [annotations] is found - */ sealed class Annotated(final override val annotations: Set) : DeclarationPredicate(), AbstractPredicate.Annotated { init { @@ -62,50 +61,12 @@ sealed class DeclarationPredicate : AbstractPredicate { } } - /** - * Matches declarations, which are annotated with [annotations] - * - * @Ann - * fun foo() {} - * - * fun bar(@Ann param: Int) {} - * - * @Ann - * class A { - * fun baz() {} - * - * class Nested { - * fun foobar() {} - * } - * } - * - * Matched symbols: [fun foo, parameter `param` from fun bar, class A] - */ class AnnotatedWith(annotations: Set) : Annotated(annotations), AbstractPredicate.AnnotatedWith { override fun accept(visitor: PredicateVisitor, data: D): R { return visitor.visitAnnotatedWith(this, data) } } - /** - * Matches declaration, if one of its containers annotated with [annotations] - * - * @Ann - * fun foo() {} - * - * fun bar(@Ann param: Int) {} - * - * @Ann - * class A { - * fun baz() {} - * - * class Nested { - * fun foobar() {} - * } - * } - * - * Matched symbols: [fun A.baz, class Nested, fun Nested.foobar] - */ class AncestorAnnotatedWith(annotations: Set) : Annotated(annotations), AbstractPredicate.AncestorAnnotatedWith { override fun accept(visitor: PredicateVisitor, data: D): R { @@ -113,25 +74,6 @@ sealed class DeclarationPredicate : AbstractPredicate { } } - /** - * Matches declaration, if its direct container annotated with [annotations] - * - * @Ann - * fun foo() {} - * - * fun bar(@Ann param: Int) {} - * - * @Ann - * class A { - * fun baz() {} - * - * class Nested { - * fun foobar() {} - * } - * } - * - * Matched symbols: [fun A.baz, class Nested] - */ class ParentAnnotatedWith(annotations: Set) : Annotated(annotations), AbstractPredicate.ParentAnnotatedWith { override fun accept(visitor: PredicateVisitor, data: D): R { @@ -139,26 +81,6 @@ sealed class DeclarationPredicate : AbstractPredicate { } } - /** - * Matches declaration, if one of its direct child declarations annotated with [annotations] - * - * @Ann - * fun foo() {} - * - * fun bar(@Ann param: Int) {} - * - * class A { - * @Ann - * fun baz() {} - * - * class Nested { - * fun foobar() {} - * } - * } - * - * Matched symbols: [fun bar, class A] - */ - class HasAnnotatedWith(annotations: Set) : Annotated(annotations), AbstractPredicate.HasAnnotatedWith { override fun accept(visitor: PredicateVisitor, data: D): R { diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate.kt index 4d9747f4283..cc8e2ae11fd 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate.kt @@ -7,6 +7,10 @@ package org.jetbrains.kotlin.fir.extensions.predicate import org.jetbrains.kotlin.fir.extensions.AnnotationFqn +/** + * For reference read KDoc to [AbstractPredicate] + * @see [AbstractPredicate] + */ sealed class LookupPredicate : AbstractPredicate { abstract override val annotations: Set final override val metaAnnotations: Set @@ -36,10 +40,6 @@ sealed class LookupPredicate : AbstractPredicate { } } - /** - * Base class for all predicates with specific annotations - * Declaration will be matched if at least one of [annotations] is found - */ sealed class Annotated(final override val annotations: Set) : LookupPredicate(), AbstractPredicate.Annotated { init { require(annotations.isNotEmpty()) { @@ -52,100 +52,24 @@ sealed class LookupPredicate : AbstractPredicate { } } - /** - * Matches declarations, which are annotated with [annotations] - * - * @Ann - * fun foo() {} - * - * fun bar(@Ann param: Int) {} - * - * @Ann - * class A { - * fun baz() {} - * - * class Nested { - * fun foobar() {} - * } - * } - * - * Matched symbols: [fun foo, parameter `param` from fun bar, class A] - */ class AnnotatedWith(annotations: Set) : Annotated(annotations), AbstractPredicate.AnnotatedWith { override fun accept(visitor: PredicateVisitor, data: D): R { return visitor.visitAnnotatedWith(this, data) } } - /** - * Matches declaration, if one of its containers annotated with [annotations] - * - * @Ann - * fun foo() {} - * - * fun bar(@Ann param: Int) {} - * - * @Ann - * class A { - * fun baz() {} - * - * class Nested { - * fun foobar() {} - * } - * } - * - * Matched symbols: [fun A.baz, class Nested, fun Nested.foobar] - */ class AncestorAnnotatedWith(annotations: Set) : Annotated(annotations), AbstractPredicate.AncestorAnnotatedWith { override fun accept(visitor: PredicateVisitor, data: D): R { return visitor.visitAncestorAnnotatedWith(this, data) } } - /** - * Matches declaration, if its direct container annotated with [annotations] - * - * @Ann - * fun foo() {} - * - * fun bar(@Ann param: Int) {} - * - * @Ann - * class A { - * fun baz() {} - * - * class Nested { - * fun foobar() {} - * } - * } - * - * Matched symbols: [fun A.baz, class Nested] - */ class ParentAnnotatedWith(annotations: Set) : Annotated(annotations), AbstractPredicate.ParentAnnotatedWith { override fun accept(visitor: PredicateVisitor, data: D): R { return visitor.visitParentAnnotatedWith(this, data) } } - /** - * Matches declaration, if one of its direct child declarations annotated with [annotations] - * - * @Ann - * fun foo() {} - * - * fun bar(@Ann param: Int) {} - * - * class A { - * @Ann - * fun baz() {} - * - * class Nested { - * fun foobar() {} - * } - * } - * - * Matched symbols: [fun bar, class A] - */ class HasAnnotatedWith(annotations: Set) : Annotated(annotations), AbstractPredicate.HasAnnotatedWith { override fun accept(visitor: PredicateVisitor, data: D): R {