diff --git a/annotations/api/annotations.api b/annotations/api/annotations.api index 64d35122e..e321412a5 100644 --- a/annotations/api/annotations.api +++ b/annotations/api/annotations.api @@ -106,6 +106,9 @@ public abstract interface annotation class com/squareup/anvil/annotations/compat public abstract fun value ()[Lcom/squareup/anvil/annotations/compat/MergeModules; } +public abstract interface annotation class com/squareup/anvil/annotations/internal/InternalAnvilApi : java/lang/annotation/Annotation { +} + public abstract interface annotation class com/squareup/anvil/annotations/internal/InternalBindingMarker : java/lang/annotation/Annotation { public abstract fun isMultibinding ()Z public abstract fun originClass ()Ljava/lang/Class; @@ -117,3 +120,19 @@ public abstract interface annotation class com/squareup/anvil/annotations/intern public abstract fun value ()[Lcom/squareup/anvil/annotations/internal/InternalBindingMarker; } +public abstract interface annotation class com/squareup/anvil/annotations/internal/InternalContributedComponentHints : java/lang/annotation/Annotation { + public abstract fun hints ()[Ljava/lang/String; +} + +public abstract interface annotation class com/squareup/anvil/annotations/internal/InternalContributedComponentHints$Container : java/lang/annotation/Annotation { + public abstract fun value ()[Lcom/squareup/anvil/annotations/internal/InternalContributedComponentHints; +} + +public abstract interface annotation class com/squareup/anvil/annotations/internal/InternalContributedModuleHints : java/lang/annotation/Annotation { + public abstract fun hints ()[Ljava/lang/String; +} + +public abstract interface annotation class com/squareup/anvil/annotations/internal/InternalContributedModuleHints$Container : java/lang/annotation/Annotation { + public abstract fun value ()[Lcom/squareup/anvil/annotations/internal/InternalContributedModuleHints; +} + diff --git a/annotations/src/main/java/com/squareup/anvil/annotations/internal/InternalAnvilApi.kt b/annotations/src/main/java/com/squareup/anvil/annotations/internal/InternalAnvilApi.kt new file mode 100644 index 000000000..3713cd344 --- /dev/null +++ b/annotations/src/main/java/com/squareup/anvil/annotations/internal/InternalAnvilApi.kt @@ -0,0 +1,7 @@ +package com.squareup.anvil.annotations.internal + +/** This Anvil API is meant for internal use and is subject to change. */ +@RequiresOptIn("This Anvil API is meant for internal use and is subject to change.") +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY) +public annotation class InternalAnvilApi diff --git a/annotations/src/main/java/com/squareup/anvil/annotations/internal/InternalContributedModuleHints.kt b/annotations/src/main/java/com/squareup/anvil/annotations/internal/InternalContributedModuleHints.kt new file mode 100644 index 000000000..0df09c6ef --- /dev/null +++ b/annotations/src/main/java/com/squareup/anvil/annotations/internal/InternalContributedModuleHints.kt @@ -0,0 +1,19 @@ +package com.squareup.anvil.annotations.internal + +/** + * An internal annotation that is used to propagate module contribution metadata to downstream compilations. + * Should not be used directly. + * + * @param hints Each hint corresponds to a single contributed module. + */ +@Target(AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.BINARY) +@Repeatable +@InternalAnvilApi +public annotation class InternalContributedModuleHints(val hints: Array) + +@Target(AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.BINARY) +@Repeatable +@InternalAnvilApi +public annotation class InternalContributedComponentHints(val hints: Array) diff --git a/build-logic/conventions/src/main/kotlin/com/squareup/anvil/conventions/BasePlugin.kt b/build-logic/conventions/src/main/kotlin/com/squareup/anvil/conventions/BasePlugin.kt index ead8bc3c5..42049158e 100644 --- a/build-logic/conventions/src/main/kotlin/com/squareup/anvil/conventions/BasePlugin.kt +++ b/build-logic/conventions/src/main/kotlin/com/squareup/anvil/conventions/BasePlugin.kt @@ -73,7 +73,10 @@ abstract class BasePlugin : Plugin { // Only add the experimental opt-in if the project has the `annotations` dependency, // otherwise the compiler will throw a warning and fail in CI. if (target.hasAnnotationDependency(sourceSetName)) { - freeCompilerArgs.add("-opt-in=com.squareup.anvil.annotations.ExperimentalAnvilApi") + freeCompilerArgs.addAll( + "-opt-in=com.squareup.anvil.annotations.ExperimentalAnvilApi", + "-opt-in=com.squareup.anvil.compiler.k2.fir.internal.InternalAnvilApi", + ) } freeCompilerArgs.addAll(extension.kotlinCompilerArgs.get()) diff --git a/compiler-k2-api/api/compiler-k2-api.api b/compiler-k2-api/api/compiler-k2-api.api index cc3250217..7aec8634f 100644 --- a/compiler-k2-api/api/compiler-k2-api.api +++ b/compiler-k2-api/api/compiler-k2-api.api @@ -1,37 +1,188 @@ +public abstract class com/squareup/anvil/compiler/k2/fir/AbstractAnvilFirProcessorFactory : com/squareup/anvil/compiler/k2/fir/AnvilFirProcessor$Factory { + public fun (Lkotlin/jvm/functions/Function1;)V + public final fun create (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext2;)Lcom/squareup/anvil/compiler/k2/fir/AnvilFirProcessor; +} + public final class com/squareup/anvil/compiler/k2/fir/AnvilFirContext { public fun (Lorg/jetbrains/kotlin/cli/common/messages/MessageCollector;)V + public final fun getLogger ()Lorg/jetbrains/kotlin/util/Logger; + public final fun getMessageCollector ()Lorg/jetbrains/kotlin/cli/common/messages/MessageCollector; +} + +public final class com/squareup/anvil/compiler/k2/fir/AnvilFirContext2 { + public fun (Lorg/jetbrains/kotlin/fir/FirSession;Lorg/jetbrains/kotlin/cli/common/messages/MessageCollector;)V + public final fun getLogger ()Lorg/jetbrains/kotlin/util/Logger; public final fun getMessageCollector ()Lorg/jetbrains/kotlin/cli/common/messages/MessageCollector; + public final fun getSession ()Lorg/jetbrains/kotlin/fir/FirSession; } public abstract class com/squareup/anvil/compiler/k2/fir/AnvilFirDeclarationGenerationExtension : org/jetbrains/kotlin/fir/extensions/FirDeclarationGenerationExtension, com/squareup/anvil/compiler/k2/fir/AnvilFirExtension { public fun (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;Lorg/jetbrains/kotlin/fir/FirSession;)V - public fun getAnvilFirContext ()Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext; + public final fun getAnvilContext ()Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext; } public abstract interface class com/squareup/anvil/compiler/k2/fir/AnvilFirDeclarationGenerationExtension$Factory : com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionFactory { } public abstract interface class com/squareup/anvil/compiler/k2/fir/AnvilFirExtension { - public abstract fun getAnvilFirContext ()Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext; } public abstract interface class com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionFactory { public abstract fun create (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;)Lorg/jetbrains/kotlin/fir/extensions/FirExtension$Factory; } +public final class com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionKt { + public static final fun getSession (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;)Lorg/jetbrains/kotlin/fir/FirSession; +} + public abstract class com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionSessionComponent : org/jetbrains/kotlin/fir/extensions/FirExtensionSessionComponent, com/squareup/anvil/compiler/k2/fir/AnvilFirExtension { public fun (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;Lorg/jetbrains/kotlin/fir/FirSession;)V - public fun getAnvilFirContext ()Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext; + public final fun getAnvilContext ()Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext; + protected final fun lazyValue (Lkotlin/jvm/functions/Function0;)Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + protected final fun map (Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; } public abstract interface class com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionSessionComponent$Factory : com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionFactory { } +public abstract class com/squareup/anvil/compiler/k2/fir/AnvilFirProcessor : com/squareup/anvil/compiler/k2/fir/HasAnvilFirContext { + protected final fun getCachesFactory ()Lorg/jetbrains/kotlin/fir/caches/FirCachesFactory; + protected final fun getSession ()Lorg/jetbrains/kotlin/fir/FirSession; + protected final fun lazySymbols (Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate;)Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + protected final fun lazyValue (Lkotlin/jvm/functions/Function0;)Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + protected final fun map (Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lkotlin/jvm/functions/Function1;)Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; +} + +public abstract interface class com/squareup/anvil/compiler/k2/fir/AnvilFirProcessor$Factory { + public abstract fun create (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext2;)Lcom/squareup/anvil/compiler/k2/fir/AnvilFirProcessor; +} + public abstract class com/squareup/anvil/compiler/k2/fir/AnvilFirSupertypeGenerationExtension : org/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension, com/squareup/anvil/compiler/k2/fir/AnvilFirExtension { public fun (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;Lorg/jetbrains/kotlin/fir/FirSession;)V - public fun getAnvilFirContext ()Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext; + public final fun getAnvilContext ()Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext; } public abstract interface class com/squareup/anvil/compiler/k2/fir/AnvilFirSupertypeGenerationExtension$Factory : com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionFactory { } +public final class com/squareup/anvil/compiler/k2/fir/BindingKey { + public fun (Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lorg/jetbrains/kotlin/name/ClassId;Lcom/squareup/anvil/compiler/k2/fir/Qualifier;)V + public final fun getContributedType ()Lorg/jetbrains/kotlin/name/ClassId; + public final fun getQualifier ()Lcom/squareup/anvil/compiler/k2/fir/Qualifier; + public final fun getScopeType ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; +} + +public final class com/squareup/anvil/compiler/k2/fir/ContributedBinding : com/squareup/anvil/compiler/k2/fir/ScopedContribution { + public fun (Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;IZZLorg/jetbrains/kotlin/name/ClassId;Lcom/squareup/anvil/compiler/k2/fir/Qualifier;)V + public final fun getBindingCallableName ()Lorg/jetbrains/kotlin/name/Name; + public final fun getBindingKey ()Lcom/squareup/anvil/compiler/k2/fir/BindingKey; + public final fun getBindingModule ()Lorg/jetbrains/kotlin/name/ClassId; + public final fun getBoundType ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public fun getContributedType ()Lorg/jetbrains/kotlin/name/ClassId; + public final fun getIgnoreQualifier ()Z + public final fun getQualifier ()Lcom/squareup/anvil/compiler/k2/fir/Qualifier; + public final fun getRank ()I + public fun getReplaces ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public fun getScopeType ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public final fun isMultibinding ()Z +} + +public final class com/squareup/anvil/compiler/k2/fir/ContributedModule : com/squareup/anvil/compiler/k2/fir/ContributedTo { + public fun (Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;)V + public fun getContributedType ()Lorg/jetbrains/kotlin/name/ClassId; + public fun getReplaces ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public fun getScopeType ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; +} + +public final class com/squareup/anvil/compiler/k2/fir/ContributedSubcomponent : com/squareup/anvil/compiler/k2/fir/ScopedContribution { + public fun (Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Ljava/util/List;Ljava/util/List;)V + public fun getContributedType ()Lorg/jetbrains/kotlin/name/ClassId; + public final fun getExclude ()Ljava/util/List; + public final fun getModules ()Ljava/util/List; + public final fun getParentScopeType ()Lorg/jetbrains/kotlin/name/ClassId; + public fun getReplaces ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public fun getScopeType ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; +} + +public final class com/squareup/anvil/compiler/k2/fir/ContributedSupertype : com/squareup/anvil/compiler/k2/fir/ContributedTo { + public fun (Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;)V + public fun getContributedType ()Lorg/jetbrains/kotlin/name/ClassId; + public fun getReplaces ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public fun getScopeType ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; +} + +public abstract interface class com/squareup/anvil/compiler/k2/fir/ContributedTo : com/squareup/anvil/compiler/k2/fir/ScopedContribution { +} + +public abstract class com/squareup/anvil/compiler/k2/fir/FlushingSupertypeProcessor : com/squareup/anvil/compiler/k2/fir/SupertypeProcessor { + public fun ()V + public fun addSupertypes (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;Ljava/util/List;Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$TypeResolveService;)Ljava/util/List; + public abstract fun generateAnnotation (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotationCall; +} + +public abstract interface class com/squareup/anvil/compiler/k2/fir/HasAnvilFirContext { + public abstract fun getAnvilContext ()Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext2; +} + +public final class com/squareup/anvil/compiler/k2/fir/MergedComponent : com/squareup/anvil/compiler/k2/fir/ScopedMerge { + public fun (Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;)V + public fun getContainingDeclaration ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public final fun getDependencies ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public fun getExclude ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public final fun getMergeAnnotationCall ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public final fun getModules ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public fun getScopeType ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public fun getTargetType ()Lorg/jetbrains/kotlin/name/ClassId; +} + +public final class com/squareup/anvil/compiler/k2/fir/PendingTopLevelClass { + public fun (Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/GeneratedDeclarationKey;Lorg/jetbrains/kotlin/descriptors/ClassKind;Lorg/jetbrains/kotlin/descriptors/Visibility;Lorg/jetbrains/kotlin/fir/caches/FirLazyValue;Lorg/jetbrains/kotlin/fir/caches/FirCachesFactory;Lorg/jetbrains/kotlin/fir/extensions/FirExtension;)V + public final fun getAnnotations ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public final fun getClassId ()Lorg/jetbrains/kotlin/name/ClassId; + public final fun getClassKind ()Lorg/jetbrains/kotlin/descriptors/ClassKind; + public final fun getGeneratedClass ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public final fun getKey ()Lorg/jetbrains/kotlin/GeneratedDeclarationKey; + public final fun getVisibility ()Lorg/jetbrains/kotlin/descriptors/Visibility; +} + +public final class com/squareup/anvil/compiler/k2/fir/Qualifier { + public static final field Companion Lcom/squareup/anvil/compiler/k2/fir/Qualifier$Companion; + public fun (Lorg/jetbrains/kotlin/name/ClassId;Ljava/lang/String;)V + public final fun getType ()Lorg/jetbrains/kotlin/name/ClassId; + public final fun getValue ()Ljava/lang/String; +} + +public final class com/squareup/anvil/compiler/k2/fir/Qualifier$Companion { +} + +public abstract interface annotation class com/squareup/anvil/compiler/k2/fir/RequiresTypesResolutionPhase : java/lang/annotation/Annotation { +} + +public abstract interface class com/squareup/anvil/compiler/k2/fir/ScopedContribution { + public abstract fun getContributedType ()Lorg/jetbrains/kotlin/name/ClassId; + public abstract fun getReplaces ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public abstract fun getScopeType ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; +} + +public abstract interface class com/squareup/anvil/compiler/k2/fir/ScopedMerge { + public abstract fun getContainingDeclaration ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public abstract fun getExclude ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public abstract fun getScopeType ()Lorg/jetbrains/kotlin/fir/caches/FirLazyValue; + public abstract fun getTargetType ()Lorg/jetbrains/kotlin/name/ClassId; +} + +public abstract class com/squareup/anvil/compiler/k2/fir/SupertypeProcessor : com/squareup/anvil/compiler/k2/fir/AnvilFirProcessor { + public fun ()V + public fun addSupertypes (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;Ljava/util/List;Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$TypeResolveService;)Ljava/util/List; + public fun computeAdditionalSupertypesForGeneratedNestedClass (Lorg/jetbrains/kotlin/fir/declarations/FirRegularClass;Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$TypeResolveService;)Ljava/util/List; + public abstract fun shouldProcess (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;)Z +} + +public abstract class com/squareup/anvil/compiler/k2/fir/TopLevelClassProcessor : com/squareup/anvil/compiler/k2/fir/AnvilFirProcessor { + public fun ()V + public fun generateFunctions (Lorg/jetbrains/kotlin/name/CallableId;Lorg/jetbrains/kotlin/fir/extensions/DeclarationGenerationContext$Member;Lorg/jetbrains/kotlin/fir/extensions/FirExtension;)Ljava/util/List; + public abstract fun generateTopLevelClassLikeDeclaration (Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/extensions/FirExtension;)Lcom/squareup/anvil/compiler/k2/fir/PendingTopLevelClass; + public fun getCallableNamesForClass (Lorg/jetbrains/kotlin/fir/symbols/impl/FirClassLikeSymbol;Lorg/jetbrains/kotlin/fir/extensions/DeclarationGenerationContext$Member;)Ljava/util/Set; + public abstract fun getTopLevelClassIds ()Ljava/util/Set; + public abstract fun hasPackage (Lorg/jetbrains/kotlin/name/FqName;)Z +} diff --git a/compiler-k2-api/build.gradle.kts b/compiler-k2-api/build.gradle.kts index c2fcd2179..623a8045b 100644 --- a/compiler-k2-api/build.gradle.kts +++ b/compiler-k2-api/build.gradle.kts @@ -19,6 +19,8 @@ dependencies { compileOnly(libs.auto.service.annotations) api(libs.kotlin.compiler) + compileOnly(project(":annotations")) + testImplementation(libs.junit.jupiter.api) testImplementation(libs.kase) testImplementation(libs.kotest.assertions.api) diff --git a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirContext.kt b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirContext.kt new file mode 100644 index 000000000..349519a2c --- /dev/null +++ b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirContext.kt @@ -0,0 +1,22 @@ +package com.squareup.anvil.compiler.k2.fir + +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.messages.toLogger +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent +import org.jetbrains.kotlin.util.Logger + +public val FirSession.anvilContext: AnvilFirContext by FirSession.sessionComponentAccessor() + +public class AnvilFirContext( + public val messageCollector: MessageCollector, + session: FirSession, +) : FirExtensionSessionComponent(session) { + public val logger: Logger by lazy(LazyThreadSafetyMode.NONE) { + messageCollector.toLogger(treatWarningsAsErrors = false) + } +} + +public interface HasAnvilFirContext { + public val anvilContext: AnvilFirContext +} diff --git a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirDeclarationGenerationExtension.kt b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirDeclarationGenerationExtension.kt index 663908ac7..b277254c9 100644 --- a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirDeclarationGenerationExtension.kt +++ b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirDeclarationGenerationExtension.kt @@ -4,9 +4,5 @@ import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension public abstract class AnvilFirDeclarationGenerationExtension( - override val anvilFirContext: AnvilFirContext, session: FirSession, -) : FirDeclarationGenerationExtension(session), - AnvilFirExtension { - public fun interface Factory : AnvilFirExtensionFactory -} +) : FirDeclarationGenerationExtension(session) diff --git a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirExtension.kt b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirExtension.kt deleted file mode 100644 index 7aa312a71..000000000 --- a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirExtension.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.squareup.anvil.compiler.k2.fir - -import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.fir.extensions.FirExtension - -public class AnvilFirContext( - public val messageCollector: MessageCollector, -) - -public sealed interface AnvilFirExtension { - public val anvilFirContext: AnvilFirContext -} - -public sealed interface AnvilFirExtensionFactory> { - public fun create(anvilFirContext: AnvilFirContext): T -} diff --git a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionSessionComponent.kt b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionSessionComponent.kt index 48fd73a8a..a8615b66e 100644 --- a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionSessionComponent.kt +++ b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionSessionComponent.kt @@ -1,13 +1,38 @@ package com.squareup.anvil.compiler.k2.fir +import com.squareup.anvil.annotations.ExperimentalAnvilApi +import com.squareup.anvil.annotations.internal.InternalAnvilApi import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.caches.FirLazyValue +import org.jetbrains.kotlin.fir.caches.firCachesFactory import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent +import org.jetbrains.kotlin.fir.extensions.predicate.LookupPredicate +import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider public abstract class AnvilFirExtensionSessionComponent( - override val anvilFirContext: AnvilFirContext, session: FirSession, -) : FirExtensionSessionComponent(session), - AnvilFirExtension { +) : FirExtensionSessionComponent(session) { - public fun interface Factory : AnvilFirExtensionFactory + protected inline fun FirLazyValue.map( + crossinline transform: (T) -> R, + ): FirLazyValue = session.firCachesFactory.createLazyValue { transform(this.getValue()) } + + protected inline fun lazyValue(crossinline initializer: () -> T): FirLazyValue { + + return session.firCachesFactory.createLazyValue { initializer() } + } + + protected inline fun lazySymbols(predicate: LookupPredicate): FirLazyValue> { + return lazyValue { + session.predicateBasedProvider.getSymbolsByPredicate(predicate) + .filterIsInstance() + } + } +} + +@InternalAnvilApi +@ExperimentalAnvilApi +public object AdditionalProcessorsHolder { + public val additionalProcessors: ThreadLocal> = + ThreadLocal.withInitial { emptyList() } } diff --git a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirProcessor.kt b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirProcessor.kt new file mode 100644 index 000000000..73dbd2d39 --- /dev/null +++ b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirProcessor.kt @@ -0,0 +1,120 @@ +package com.squareup.anvil.compiler.k2.fir + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.caches.FirCachesFactory +import org.jetbrains.kotlin.fir.caches.FirLazyValue +import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.fir.extensions.ExperimentalTopLevelDeclarationsGenerationApi +import org.jetbrains.kotlin.fir.extensions.FirExtension +import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension +import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension.TypeResolveService +import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext +import org.jetbrains.kotlin.fir.extensions.predicate.LookupPredicate +import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name + +public sealed class AnvilFirProcessor : HasAnvilFirContext { + + protected val session: FirSession get() = anvilContext.session + protected val cachesFactory: FirCachesFactory get() = session.firCachesFactory + + protected inline fun FirLazyValue.map( + crossinline transform: (T) -> R, + ): FirLazyValue = lazyValue { transform(this.getValue()) } + + protected inline fun lazyValue(crossinline initializer: () -> T): FirLazyValue { + return cachesFactory.createLazyValue { initializer() } + } + + protected fun lazySymbols( + predicate: LookupPredicate, + ): FirLazyValue>> = lazyValue { + session.predicateBasedProvider.getSymbolsByPredicate(predicate) + } + + protected inline fun lazySymbolsOf( + predicate: LookupPredicate, + ): FirLazyValue> = lazySymbols(predicate).map { it.filterIsInstance() } + + public fun interface Factory { + public fun create(anvilFirContext: AnvilFirContext): AnvilFirProcessor + } +} + +public abstract class AbstractAnvilFirProcessorFactory( + private val initializer: (AnvilFirContext) -> AnvilFirProcessor, +) : AnvilFirProcessor.Factory { + final override fun create(anvilFirContext: AnvilFirContext): AnvilFirProcessor = + initializer(anvilFirContext) +} + +public abstract class TopLevelClassProcessor : AnvilFirProcessor() { + + @ExperimentalTopLevelDeclarationsGenerationApi + public abstract fun getTopLevelClassIds(): Set + public abstract fun hasPackage(packageFqName: FqName): Boolean + + @ExperimentalTopLevelDeclarationsGenerationApi + public abstract fun generateTopLevelClassLikeDeclaration( + classId: ClassId, + firExtension: FirExtension, + ): PendingTopLevelClass + + public open fun getCallableNamesForClass( + classSymbol: FirClassLikeSymbol<*>, + context: MemberGenerationContext, + ): Set = emptySet() + + public open fun generateFunctions( + callableId: CallableId, + context: MemberGenerationContext?, + firExtension: FirExtension, + ): List = emptyList() +} + +public abstract class SupertypeProcessor : AnvilFirProcessor() { + public abstract fun shouldProcess(declaration: FirClassLikeDeclaration): Boolean + public open fun addSupertypes( + classLikeDeclaration: FirClassLikeDeclaration, + resolvedSupertypes: List, + typeResolver: FirSupertypeGenerationExtension.TypeResolveService, + ): List = emptyList() + + public open fun computeAdditionalSupertypesForGeneratedNestedClass( + klass: FirRegularClass, + typeResolver: FirSupertypeGenerationExtension.TypeResolveService, + ): List = emptyList() +} + +public abstract class FlushingSupertypeProcessor : SupertypeProcessor() { + + @OptIn(RequiresTypesResolutionPhase::class) + public override fun addSupertypes( + classLikeDeclaration: FirClassLikeDeclaration, + resolvedSupertypes: List, + typeResolver: TypeResolveService, + ): List { + + classLikeDeclaration.replaceAnnotations( + classLikeDeclaration.annotations + generateAnnotation(classLikeDeclaration), + ) + + return emptyList() + } + + @RequiresTypesResolutionPhase + public abstract fun generateAnnotation( + classLikeDeclaration: FirClassLikeDeclaration, + ): FirAnnotationCall +} diff --git a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirSupertypeGenerationExtension.kt b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirSupertypeGenerationExtension.kt deleted file mode 100644 index 3720009a7..000000000 --- a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirSupertypeGenerationExtension.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.squareup.anvil.compiler.k2.fir - -import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension - -public abstract class AnvilFirSupertypeGenerationExtension( - override val anvilFirContext: AnvilFirContext, - session: FirSession, -) : FirSupertypeGenerationExtension(session), - AnvilFirExtension { - public fun interface Factory : AnvilFirExtensionFactory -} diff --git a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/PendingTopLevelClass.kt b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/PendingTopLevelClass.kt new file mode 100644 index 000000000..2368ca712 --- /dev/null +++ b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/PendingTopLevelClass.kt @@ -0,0 +1,37 @@ +package com.squareup.anvil.compiler.k2.fir + +import org.jetbrains.kotlin.GeneratedDeclarationKey +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.fir.caches.FirCachesFactory +import org.jetbrains.kotlin.fir.caches.FirLazyValue +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.extensions.ExperimentalTopLevelDeclarationsGenerationApi +import org.jetbrains.kotlin.fir.extensions.FirExtension +import org.jetbrains.kotlin.fir.plugin.createTopLevelClass +import org.jetbrains.kotlin.name.ClassId + +@OptIn(ExperimentalTopLevelDeclarationsGenerationApi::class) +public class PendingTopLevelClass( + public val classId: ClassId, + public val key: GeneratedDeclarationKey, + public val classKind: ClassKind, + public val visibility: Visibility, + public val annotations: FirLazyValue>, + cachesFactory: FirCachesFactory, + firExtension: FirExtension, +) { + + public val generatedClass: FirLazyValue = cachesFactory.createLazyValue { + firExtension.createTopLevelClass( + classId = classId, + key = key, + classKind = classKind, + ) { + visibility = this@PendingTopLevelClass.visibility + }.apply { + replaceAnnotations(this@PendingTopLevelClass.annotations.getValue()) + } + } +} diff --git a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/PendingTopLevelProperty.kt b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/PendingTopLevelProperty.kt new file mode 100644 index 000000000..b48d53f93 --- /dev/null +++ b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/PendingTopLevelProperty.kt @@ -0,0 +1,36 @@ +package com.squareup.anvil.compiler.k2.fir + +import org.jetbrains.kotlin.GeneratedDeclarationKey +import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.fir.caches.FirCachesFactory +import org.jetbrains.kotlin.fir.caches.FirLazyValue +import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.extensions.ExperimentalTopLevelDeclarationsGenerationApi +import org.jetbrains.kotlin.fir.extensions.FirExtension +import org.jetbrains.kotlin.fir.plugin.createTopLevelProperty +import org.jetbrains.kotlin.name.CallableId + +@OptIn(ExperimentalTopLevelDeclarationsGenerationApi::class) +internal class PendingTopLevelProperty( + val callableId: CallableId, + val key: GeneratedDeclarationKey, + val visibility: Visibility, + val annotations: FirLazyValue>, + cachesFactory: FirCachesFactory, + firExtension: FirExtension, +) { + + val generatedProperty: FirLazyValue = cachesFactory.createLazyValue { + firExtension.createTopLevelProperty( + key = key, + callableId = callableId, + returnType = firExtension.session.builtinTypes.unitType.coneType, + ) { + val ctx = this@createTopLevelProperty + ctx.visibility = this@PendingTopLevelProperty.visibility + }.apply { + replaceAnnotations(this@PendingTopLevelProperty.annotations.getValue()) + } + } +} diff --git a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/RequiresTypesResolutionPhase.kt b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/RequiresTypesResolutionPhase.kt new file mode 100644 index 000000000..84d238ed1 --- /dev/null +++ b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/RequiresTypesResolutionPhase.kt @@ -0,0 +1,5 @@ +package com.squareup.anvil.compiler.k2.fir + +@RequiresOptIn("Cannot be called before supertype resolution", RequiresOptIn.Level.ERROR) +@Retention(AnnotationRetention.BINARY) +public annotation class RequiresTypesResolutionPhase diff --git a/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/ScopedContribution.kt b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/ScopedContribution.kt new file mode 100644 index 000000000..8c2d0d457 --- /dev/null +++ b/compiler-k2-api/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/ScopedContribution.kt @@ -0,0 +1,114 @@ +package com.squareup.anvil.compiler.k2.fir + +import org.jetbrains.kotlin.fir.caches.FirLazyValue +import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration +import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name + +public sealed interface ScopedMerge { + public val scopeType: FirLazyValue + public val targetType: ClassId + public val containingDeclaration: FirLazyValue + + public val exclude: FirLazyValue> +} + +public class MergedComponent( + override val scopeType: FirLazyValue, + override val targetType: ClassId, + public val modules: FirLazyValue>, + public val dependencies: FirLazyValue>, + override val exclude: FirLazyValue>, + override val containingDeclaration: FirLazyValue, + public val mergeAnnotationCall: FirLazyValue, +) : ScopedMerge + +public sealed interface ScopedContribution { + public val scopeType: FirLazyValue + public val contributedType: ClassId + + public val replaces: FirLazyValue> +} + +public sealed interface ContributedTo : ScopedContribution + +/** + * Something with both `@ContributesTo` and `@Module` annotations. + * + * @see com.squareup.anvil.annotations.ContributesTo + */ +public class ContributedModule( + override val scopeType: FirLazyValue, + override val contributedType: ClassId, + override val replaces: FirLazyValue>, +) : ContributedTo + +/** + * An interface or abstract class with `@ContributesTo` annotation but *not* `@Module`. + * + * @see com.squareup.anvil.annotations.ContributesTo + */ +public class ContributedSupertype( + override val scopeType: FirLazyValue, + override val contributedType: ClassId, + override val replaces: FirLazyValue>, +) : ContributedTo + +/** + * An interface or abstract class with `@ContributesSubcomponent` annotation. + * + * @see com.squareup.anvil.annotations.ContributesSubcomponent + */ +public class ContributedSubcomponent( + override val scopeType: FirLazyValue, + public val parentScopeType: ClassId, + override val contributedType: ClassId, + override val replaces: FirLazyValue>, + public val modules: List, + public val exclude: List, +) : ScopedContribution + +/** + * @see com.squareup.anvil.annotations.ContributesBinding + * @see com.squareup.anvil.annotations.ContributesMultibinding + */ +public class ContributedBinding( + override val scopeType: FirLazyValue, + public val boundType: FirLazyValue, + override val contributedType: ClassId, + override val replaces: FirLazyValue>, + public val rank: Int, + public val ignoreQualifier: Boolean, + public val isMultibinding: Boolean, + public val bindingModule: ClassId, + // val bindingModuleSymbol: FirClassSymbol<*>, + public val qualifier: Qualifier?, +) : ScopedContribution { + public val bindingKey: BindingKey by lazy(LazyThreadSafetyMode.NONE) { + BindingKey( + scopeType = scopeType, + contributedType = contributedType, + qualifier = qualifier, + ) + } + public val bindingCallableName: Name by lazy(LazyThreadSafetyMode.NONE) { + val relativeName = boundType.getValue().relativeClassName.asString().replace('.', '_') + Name.identifier("bind_$relativeName") + } +} + +/** A unique composite key for a contributed binding. */ +public class BindingKey( + public val scopeType: FirLazyValue, + public val contributedType: ClassId, + public val qualifier: Qualifier?, +) + +public class Qualifier( + public val type: ClassId, + public val value: String?, +) { + + public companion object +} diff --git a/compiler-k2-utils/api/compiler-k2-utils.api b/compiler-k2-utils/api/compiler-k2-utils.api index 1cdd27d01..e7b58d41c 100644 --- a/compiler-k2-utils/api/compiler-k2-utils.api +++ b/compiler-k2-utils/api/compiler-k2-utils.api @@ -5,10 +5,21 @@ public final class com/squareup/anvil/compiler/k2/utils/fir/AnvilPredicates { public final fun getHasAnvilContributesMultibinding ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; public final fun getHasAnvilContributesSubcomponent ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; public final fun getHasAnvilContributesTo ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; + public final fun getHasAnvilInternalContributedComponentHints ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; + public final fun getHasAnvilInternalContributedModuleHints ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; + public final fun getHasAnvilMergeComponent ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; + public final fun getHasAnvilMergeInterfaces ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; + public final fun getHasAnvilMergeModules ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; + public final fun getHasAnvilMergeSubcomponent ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; public final fun getHasAnyAnvilContributes ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; + public final fun getHasAnyAnvilMerge ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; + public final fun getHasAnyDaggerAnnotation ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; + public final fun getHasDaggerBinds ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; + public final fun getHasDaggerComponent ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; + public final fun getHasDaggerModule ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; + public final fun getHasDaggerProvides ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; + public final fun getHasDaggerSubcomponent ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; public final fun getHasInjectAnnotation ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; - public final fun getHasMergeComponentAnnotation ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; - public final fun getHasModuleAnnotation ()Lorg/jetbrains/kotlin/fir/extensions/predicate/LookupPredicate; public final fun getHasQualifierMetaAnnotation ()Lorg/jetbrains/kotlin/fir/extensions/predicate/DeclarationPredicate; } @@ -22,23 +33,36 @@ public final class com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationCallBui public static final fun setAnnotationType (Lorg/jetbrains/kotlin/fir/expressions/builder/FirAnnotationCallBuilder;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/psi/KtPsiFactory;)V } +public final class com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationContainerUtilsKt { + public static final fun requireAnnotation (Lorg/jetbrains/kotlin/fir/FirAnnotationContainer;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/FirSession;)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation; + public static final fun requireAnnotationCall (Lorg/jetbrains/kotlin/fir/FirAnnotationContainer;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/FirSession;)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotationCall; +} + public final class com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationUtilsKt { public static final fun argumentAt (Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation;Lorg/jetbrains/kotlin/name/Name;IZ)Lorg/jetbrains/kotlin/fir/expressions/FirExpression; + public static final fun boundTypeArgumentOrNull (Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation;Lorg/jetbrains/kotlin/fir/FirSession;)Lorg/jetbrains/kotlin/fir/expressions/FirGetClassCall; public static final fun classId (Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation;Lorg/jetbrains/kotlin/fir/FirSession;)Lorg/jetbrains/kotlin/name/ClassId; public static final fun classListArgumentAt (Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation;Lorg/jetbrains/kotlin/name/Name;I)Ljava/util/List; public static final fun createFirAnnotation (Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/expressions/FirAnnotationArgumentMapping;Lorg/jetbrains/kotlin/KtSourceElement;Lorg/jetbrains/kotlin/descriptors/annotations/AnnotationUseSiteTarget;)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation; public static synthetic fun createFirAnnotation$default (Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/expressions/FirAnnotationArgumentMapping;Lorg/jetbrains/kotlin/KtSourceElement;Lorg/jetbrains/kotlin/descriptors/annotations/AnnotationUseSiteTarget;ILjava/lang/Object;)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation; + public static final fun createFirAnnotationCall (Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/symbols/FirBasedSymbol;Lorg/jetbrains/kotlin/fir/expressions/FirArgumentList;Lorg/jetbrains/kotlin/KtSourceElement;Lorg/jetbrains/kotlin/descriptors/annotations/AnnotationUseSiteTarget;Lorg/jetbrains/kotlin/psi/KtPsiFactory;)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotationCall; + public static synthetic fun createFirAnnotationCall$default (Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/symbols/FirBasedSymbol;Lorg/jetbrains/kotlin/fir/expressions/FirArgumentList;Lorg/jetbrains/kotlin/KtSourceElement;Lorg/jetbrains/kotlin/descriptors/annotations/AnnotationUseSiteTarget;Lorg/jetbrains/kotlin/psi/KtPsiFactory;ILjava/lang/Object;)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotationCall; + public static final fun createOptInAnnotation (Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/FirSession;)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation; + public static final fun rankArgumentOrNull (Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation;Lorg/jetbrains/kotlin/fir/FirSession;)Ljava/lang/Integer; + public static final fun replacesArgumentOrNull (Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation;Lorg/jetbrains/kotlin/fir/FirSession;)Ljava/util/List; public static final fun requireArgumentAt (Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation;Lorg/jetbrains/kotlin/name/Name;IZ)Lorg/jetbrains/kotlin/fir/expressions/FirExpression; public static final fun requireClassId (Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation;Lorg/jetbrains/kotlin/fir/FirSession;)Lorg/jetbrains/kotlin/name/ClassId; + public static final fun requireReplacesArgument (Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation;Lorg/jetbrains/kotlin/fir/FirSession;)Ljava/util/List; public static final fun requireScopeArgument (Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation;)Lorg/jetbrains/kotlin/fir/expressions/FirGetClassCall; public static final fun requireScopeArgument (Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation;Lorg/jetbrains/kotlin/fir/FirSession;)Lorg/jetbrains/kotlin/fir/types/ConeKotlinType; public static final fun requireScopeArgument (Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation;Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$TypeResolveService;)Lorg/jetbrains/kotlin/fir/types/ConeKotlinType; } public final class com/squareup/anvil/compiler/k2/utils/fir/FirBasedSymbolUtilsKt { - public static final fun hasAnnotation (Lorg/jetbrains/kotlin/fir/symbols/FirBasedSymbol;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/FirSession;)Z - public static final fun requireAnnotation (Lorg/jetbrains/kotlin/fir/symbols/FirBasedSymbol;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/FirSession;)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation; - public static final fun requireAnnotationCall (Lorg/jetbrains/kotlin/fir/symbols/FirBasedSymbol;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/FirSession;)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotationCall; + public static final fun getResolvedAnnotation (Lorg/jetbrains/kotlin/fir/symbols/FirBasedSymbol;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/FirSession;Z)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation; + public static final fun hasAnnotation (Lorg/jetbrains/kotlin/fir/symbols/FirBasedSymbol;Lorg/jetbrains/kotlin/fir/FirSession;Lorg/jetbrains/kotlin/name/ClassId;)Z + public static final fun requireAnnotation (Lorg/jetbrains/kotlin/fir/symbols/FirBasedSymbol;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/FirSession;Z)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation; + public static final fun requireAnnotationCall (Lorg/jetbrains/kotlin/fir/symbols/FirBasedSymbol;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/FirSession;Z)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotationCall; } public final class com/squareup/anvil/compiler/k2/utils/fir/FirClassLikeSymbolUtilsKt { @@ -47,22 +71,38 @@ public final class com/squareup/anvil/compiler/k2/utils/fir/FirClassLikeSymbolUt public static final fun contributesToScope (Lorg/jetbrains/kotlin/fir/symbols/impl/FirClassLikeSymbol;Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/FirSession;Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$TypeResolveService;)Z public static final fun contributesToScope (Lorg/jetbrains/kotlin/fir/symbols/impl/FirClassLikeSymbol;Lorg/jetbrains/kotlin/name/FqName;Lorg/jetbrains/kotlin/fir/FirSession;Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$TypeResolveService;)Z public static final fun fqName (Lorg/jetbrains/kotlin/fir/symbols/impl/FirClassLikeSymbol;)Lorg/jetbrains/kotlin/name/FqName; + public static final fun getContributesBindingAnnotations (Lorg/jetbrains/kotlin/fir/symbols/impl/FirClassLikeSymbol;Lorg/jetbrains/kotlin/fir/FirSession;)Ljava/util/List; public static final fun toGetClassCall (Lorg/jetbrains/kotlin/fir/symbols/impl/FirClassLikeSymbol;)Lorg/jetbrains/kotlin/fir/expressions/FirGetClassCall; public static final fun toKClassRef (Lorg/jetbrains/kotlin/fir/symbols/impl/FirClassLikeSymbol;)Lorg/jetbrains/kotlin/fir/types/ConeClassLikeType; } +public final class com/squareup/anvil/compiler/k2/utils/fir/FirFileUtilsKt { + public static final fun createSyntheticFile (Lorg/jetbrains/kotlin/fir/FirSession;Lorg/jetbrains/kotlin/fir/declarations/FirDeclarationOrigin;Lorg/jetbrains/kotlin/name/FqName;Ljava/lang/String;Ljava/util/List;)Lorg/jetbrains/kotlin/fir/declarations/FirFile; + public static final fun wrapInSyntheticFile (Lorg/jetbrains/kotlin/fir/declarations/FirProperty;Lorg/jetbrains/kotlin/fir/FirSession;)Lorg/jetbrains/kotlin/fir/declarations/FirProperty; + public static final fun wrapInSyntheticFile (Lorg/jetbrains/kotlin/fir/declarations/FirRegularClass;Lorg/jetbrains/kotlin/fir/FirSession;)Lorg/jetbrains/kotlin/fir/declarations/FirRegularClass; +} + public final class com/squareup/anvil/compiler/k2/utils/fir/FirGetClassCallUtilsKt { public static final fun requireTargetClassId (Lorg/jetbrains/kotlin/fir/expressions/FirGetClassCall;)Lorg/jetbrains/kotlin/name/ClassId; + public static final fun requireTargetClassId (Lorg/jetbrains/kotlin/fir/expressions/FirGetClassCall;Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$TypeResolveService;)Lorg/jetbrains/kotlin/name/ClassId; public static final fun requireTargetType (Lorg/jetbrains/kotlin/fir/expressions/FirGetClassCall;)Lorg/jetbrains/kotlin/fir/types/ConeKotlinType; public static final fun resolveConeType (Lorg/jetbrains/kotlin/fir/expressions/FirGetClassCall;Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$TypeResolveService;)Lorg/jetbrains/kotlin/fir/types/ConeKotlinType; public static final fun userTypeRef (Lorg/jetbrains/kotlin/fir/expressions/FirGetClassCall;)Lorg/jetbrains/kotlin/fir/types/FirUserTypeRef; } +public final class com/squareup/anvil/compiler/k2/utils/fir/FirNamedArgumentExpressionUtilsKt { + public static final fun createClassListArgument (Lorg/jetbrains/kotlin/name/Name;Ljava/util/List;Lorg/jetbrains/kotlin/fir/FirSession;)Lorg/jetbrains/kotlin/fir/expressions/FirNamedArgumentExpression; +} + public final class com/squareup/anvil/compiler/k2/utils/fir/FirPropertyAccessExpressionUtilsKt { public static final fun getQualifierName (Lorg/jetbrains/kotlin/fir/expressions/FirPropertyAccessExpression;)Lorg/jetbrains/kotlin/name/Name; public static final fun qualifierSegmentsWithSelf (Lorg/jetbrains/kotlin/fir/expressions/FirPropertyAccessExpression;)Ljava/util/List; } +public final class com/squareup/anvil/compiler/k2/utils/fir/FirRegularClassSymbolUtilsKt { + public static final fun resolvedTypeRef (Lorg/jetbrains/kotlin/fir/symbols/impl/FirRegularClassSymbol;)Lorg/jetbrains/kotlin/fir/types/FirResolvedTypeRef; +} + public final class com/squareup/anvil/compiler/k2/utils/fir/FirUserTypeRefUtilsKt { public static final fun createUserTypeRef (Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/KtSourceElement;Z)Lorg/jetbrains/kotlin/fir/types/FirUserTypeRef; public static synthetic fun createUserTypeRef$default (Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/KtSourceElement;ZILjava/lang/Object;)Lorg/jetbrains/kotlin/fir/types/FirUserTypeRef; @@ -77,10 +117,15 @@ public final class com/squareup/anvil/compiler/k2/utils/fir/ResolveUtilsKt { public final class com/squareup/anvil/compiler/k2/utils/names/ClassIds { public static final field INSTANCE Lcom/squareup/anvil/compiler/k2/utils/names/ClassIds; + public final fun anvilContributedComponents (Ljava/util/List;)Lorg/jetbrains/kotlin/name/ClassId; + public final fun anvilContributedModules (Ljava/util/List;)Lorg/jetbrains/kotlin/name/ClassId; public final fun getAnvilContributesBinding ()Lorg/jetbrains/kotlin/name/ClassId; public final fun getAnvilContributesMultibinding ()Lorg/jetbrains/kotlin/name/ClassId; public final fun getAnvilContributesSubcomponent ()Lorg/jetbrains/kotlin/name/ClassId; public final fun getAnvilContributesTo ()Lorg/jetbrains/kotlin/name/ClassId; + public final fun getAnvilInternalAnvilApi ()Lorg/jetbrains/kotlin/name/ClassId; + public final fun getAnvilInternalContributedComponentHints ()Lorg/jetbrains/kotlin/name/ClassId; + public final fun getAnvilInternalContributedModuleHints ()Lorg/jetbrains/kotlin/name/ClassId; public final fun getAnvilMergeComponent ()Lorg/jetbrains/kotlin/name/ClassId; public final fun getAnvilMergeInterfaces ()Lorg/jetbrains/kotlin/name/ClassId; public final fun getAnvilMergeModules ()Lorg/jetbrains/kotlin/name/ClassId; @@ -91,6 +136,7 @@ public final class com/squareup/anvil/compiler/k2/utils/names/ClassIds { public final fun getDaggerLazy ()Lorg/jetbrains/kotlin/name/ClassId; public final fun getDaggerModule ()Lorg/jetbrains/kotlin/name/ClassId; public final fun getDaggerProvider ()Lorg/jetbrains/kotlin/name/ClassId; + public final fun getDaggerProvides ()Lorg/jetbrains/kotlin/name/ClassId; public final fun getDaggerSubcomponent ()Lorg/jetbrains/kotlin/name/ClassId; public final fun getJavaxInject ()Lorg/jetbrains/kotlin/name/ClassId; public final fun getJavaxProvider ()Lorg/jetbrains/kotlin/name/ClassId; @@ -115,10 +161,20 @@ public final class com/squareup/anvil/compiler/k2/utils/names/ClassIdsKt { public static final fun sibling (Lorg/jetbrains/kotlin/name/ClassId;Ljava/lang/String;)Lorg/jetbrains/kotlin/name/ClassId; } +public final class com/squareup/anvil/compiler/k2/utils/names/FqNames { + public static final field INSTANCE Lcom/squareup/anvil/compiler/k2/utils/names/FqNames; + public final fun getAnvilHintPackage ()Lorg/jetbrains/kotlin/name/FqName; +} + public final class com/squareup/anvil/compiler/k2/utils/names/Names { public static final field INSTANCE Lcom/squareup/anvil/compiler/k2/utils/names/Names; + public final fun getBoundType ()Lorg/jetbrains/kotlin/name/Name; public final fun getDependencies ()Lorg/jetbrains/kotlin/name/Name; + public final fun getExclude ()Lorg/jetbrains/kotlin/name/Name; + public final fun getHints ()Lorg/jetbrains/kotlin/name/Name; public final fun getModules ()Lorg/jetbrains/kotlin/name/Name; + public final fun getRank ()Lorg/jetbrains/kotlin/name/Name; + public final fun getReplaces ()Lorg/jetbrains/kotlin/name/Name; public final fun getScope ()Lorg/jetbrains/kotlin/name/Name; } diff --git a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/AnvilPredicates.kt b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/AnvilPredicates.kt index e6749edea..90cee727c 100644 --- a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/AnvilPredicates.kt +++ b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/AnvilPredicates.kt @@ -8,6 +8,9 @@ import org.jetbrains.kotlin.name.ClassId public object AnvilPredicates { + /* + Anvil Contributes annotations + */ public val contributedModule: LookupPredicate get() = LookupPredicate.create { annotated(ClassIds.anvilContributesTo.asSingleFqName()) @@ -26,12 +29,58 @@ public object AnvilPredicates { .or(hasAnvilContributesBinding) .or(hasAnvilContributesMultibinding) .or(hasAnvilContributesSubcomponent) - public val hasInjectAnnotation: LookupPredicate - get() = ClassIds.javaxInject.lookupPredicateAnnotated() - public val hasMergeComponentAnnotation: LookupPredicate + + /* + Anvil hint annotations + */ + public val hasAnvilInternalContributedModuleHints: LookupPredicate + get() = ClassIds.anvilInternalContributedModuleHints.lookupPredicateAnnotated() + public val hasAnvilInternalContributedComponentHints: LookupPredicate + get() = ClassIds.anvilInternalContributedComponentHints.lookupPredicateAnnotated() + + /* + Anvil Merge annotations + */ + public val hasAnvilMergeComponent: LookupPredicate get() = ClassIds.anvilMergeComponent.lookupPredicateAnnotated() - public val hasModuleAnnotation: LookupPredicate + public val hasAnvilMergeSubcomponent: LookupPredicate + get() = ClassIds.anvilMergeSubcomponent.lookupPredicateAnnotated() + public val hasAnvilMergeModules: LookupPredicate + get() = ClassIds.anvilMergeModules.lookupPredicateAnnotated() + public val hasAnvilMergeInterfaces: LookupPredicate + get() = ClassIds.anvilMergeInterfaces.lookupPredicateAnnotated() + public val hasAnyAnvilMerge: LookupPredicate + get() = hasAnvilMergeComponent + .or(hasAnvilMergeSubcomponent) + .or(hasAnvilMergeModules) + .or(hasAnvilMergeInterfaces) + + /* + Dagger annotations + */ + public val hasDaggerModule: LookupPredicate get() = ClassIds.daggerModule.lookupPredicateAnnotated() + public val hasDaggerBinds: LookupPredicate + get() = ClassIds.daggerBinds.lookupPredicateAnnotated() + public val hasDaggerComponent: LookupPredicate + get() = ClassIds.daggerComponent.lookupPredicateAnnotated() + public val hasDaggerSubcomponent: LookupPredicate + get() = ClassIds.daggerSubcomponent.lookupPredicateAnnotated() + public val hasDaggerProvides: LookupPredicate + get() = ClassIds.daggerProvides.lookupPredicateAnnotated() + + public val hasAnyDaggerAnnotation: LookupPredicate + get() = hasDaggerModule + .or(hasDaggerBinds) + .or(hasDaggerComponent) + .or(hasDaggerSubcomponent) + .or(hasDaggerProvides) + + /* + JSR-330 annotations + */ + public val hasInjectAnnotation: LookupPredicate + get() = ClassIds.javaxInject.lookupPredicateAnnotated() public val hasQualifierMetaAnnotation: DeclarationPredicate get() = DeclarationPredicate.create { @@ -42,5 +91,10 @@ public object AnvilPredicates { } private fun ClassId.lookupPredicateAnnotated(): LookupPredicate = - LookupPredicate.create { annotated(this@lookupPredicateAnnotated.asSingleFqName()) } + LookupPredicate.create { + annotated(this@lookupPredicateAnnotated.asSingleFqName()) + } + + private fun ClassId.declarationPredicateAnnotated(): DeclarationPredicate = + DeclarationPredicate.create { annotated(this@declarationPredicateAnnotated.asSingleFqName()) } } diff --git a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationCallBuilderUtils.kt b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationCallBuilderUtils.kt index 2a45a36e9..196eec054 100644 --- a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationCallBuilderUtils.kt +++ b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationCallBuilderUtils.kt @@ -1,8 +1,11 @@ package com.squareup.anvil.compiler.k2.utils.fir import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.fir.expressions.FirAnnotationResolvePhase import org.jetbrains.kotlin.fir.expressions.builder.FirAnnotationCallBuilder import org.jetbrains.kotlin.fir.references.builder.buildSimpleNamedReference +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.constructClassLikeType import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.resolve.calls.util.asCallableReferenceExpression @@ -16,15 +19,17 @@ public fun FirAnnotationCallBuilder.setAnnotationType( ?.createTypeArgument(newType.asFqNameString()) ?.typeReference - annotationTypeRef = createUserTypeRef( - classId = newType, - sourceElement = componentTypeRef?.toKtPsiSourceElement(KtFakeSourceElementKind.PluginGenerated), - nullable = false, - ) + annotationTypeRef = buildResolvedTypeRef { + coneType = newType.constructClassLikeType() + source = componentTypeRef?.toKtPsiSourceElement(KtFakeSourceElementKind.PluginGenerated) + } + calleeReference = buildSimpleNamedReference { name = newType.shortClassName source = componentTypeRef?.asCallableReferenceExpression() ?.callableReference ?.toKtPsiSourceElement(KtFakeSourceElementKind.PluginGenerated) } + + annotationResolvePhase = FirAnnotationResolvePhase.Types } diff --git a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationContainerUtils.kt b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationContainerUtils.kt new file mode 100644 index 000000000..c1568402a --- /dev/null +++ b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationContainerUtils.kt @@ -0,0 +1,29 @@ +package com.squareup.anvil.compiler.k2.utils.fir + +import org.jetbrains.kotlin.fir.FirAnnotationContainer +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.name.ClassId + +@Suppress("DeprecatedCallableAddReplaceWith") +@Deprecated( + "Resolve annotations from the container's symbol instead", + level = DeprecationLevel.ERROR, +) +public fun FirAnnotationContainer.requireAnnotationCall( + classId: ClassId, + session: FirSession, +): FirAnnotationCall = + throw UnsupportedOperationException("Resolve annotations from the container's symbol instead") + +@Suppress("DeprecatedCallableAddReplaceWith") +@Deprecated( + "Resolve annotations from the container's symbol instead", + level = DeprecationLevel.ERROR, +) +public fun FirAnnotationContainer.requireAnnotation( + classId: ClassId, + session: FirSession, +): FirAnnotation = + throw UnsupportedOperationException("Resolve annotations from the container's symbol instead") diff --git a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationUtils.kt b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationUtils.kt index 0e8b4e455..998ca7566 100644 --- a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationUtils.kt +++ b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirAnnotationUtils.kt @@ -1,32 +1,44 @@ package com.squareup.anvil.compiler.k2.utils.fir +import com.squareup.anvil.compiler.k2.utils.names.ClassIds import com.squareup.anvil.compiler.k2.utils.names.Names import com.squareup.anvil.compiler.k2.utils.names.classId import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.evaluateAs +import org.jetbrains.kotlin.fir.declarations.getKClassArgument import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.expressions.FirAnnotationArgumentMapping import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.fir.expressions.FirArgumentList import org.jetbrains.kotlin.fir.expressions.FirArrayLiteral +import org.jetbrains.kotlin.fir.expressions.FirEmptyArgumentList import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirGetClassCall +import org.jetbrains.kotlin.fir.expressions.FirLiteralExpression import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression import org.jetbrains.kotlin.fir.expressions.arguments import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotation +import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationArgumentMapping +import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCall import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyAnnotationArgumentMapping import org.jetbrains.kotlin.fir.expressions.unwrapArgument import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension +import org.jetbrains.kotlin.fir.render import org.jetbrains.kotlin.fir.resolve.fqName import org.jetbrains.kotlin.fir.resolve.toRegularClassSymbol +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.constructClassLikeType +import org.jetbrains.kotlin.fir.types.type import org.jetbrains.kotlin.fir.utils.exceptions.withFirEntry import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.resolve.checkers.OptInNames import org.jetbrains.kotlin.utils.exceptions.KotlinIllegalArgumentExceptionWithAttachments import org.jetbrains.kotlin.utils.exceptions.checkWithAttachment import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment @@ -41,23 +53,73 @@ public fun FirAnnotation.requireClassId(session: FirSession): ClassId { return expandedSymbol.classId } -public fun FirAnnotation.requireScopeArgument(session: FirSession): ConeKotlinType { +public fun FirAnnotation.replacesArgumentOrNull(session: FirSession): List? { + return classListArgumentAt( + name = Names.replaces, + index = replacesIndex(requireClassId(session)), + ) +} - val expression = requireArgumentAt( - name = Names.scope, - index = 0, +public fun FirAnnotation.boundTypeArgumentOrNull(session: FirSession): FirGetClassCall? { + return argumentAt( + name = Names.replaces, + index = replacesIndex(requireClassId(session)), unwrapNamedArguments = true, - ) - val evaluated = expression.evaluateAs(session) - ?: errorWithAttachment("Scope argument is not a FirGetClassCall: $expression") { - withFirEntry("scope argument expression", expression) + )?.let { it as FirGetClassCall } +} + +public fun FirAnnotation.rankArgumentOrNull(session: FirSession): Int? { + val arg = argumentAt( + name = Names.rank, + index = rankIndex(requireClassId(session)), + unwrapNamedArguments = true, + ) ?: return null + return arg.evaluateAs(session)?.value as? Int +} + +public fun FirAnnotation.requireReplacesArgument(session: FirSession): List { + return classListArgumentAt( + name = Names.replaces, + index = replacesIndex(requireClassId(session)), + ).orEmpty() +} + +private fun rankIndex(annotationClassId: ClassId): Int { + return when (annotationClassId) { + ClassIds.anvilContributesBinding, ClassIds.anvilContributesMultibinding -> 6 + else -> throw NotImplementedError( + "Couldn't find index of rank argument for $annotationClassId.", + ) + } +} + +private fun replacesIndex(annotationClassId: ClassId): Int { + return when (annotationClassId) { + ClassIds.anvilContributesTo -> 1 + ClassIds.anvilContributesBinding, ClassIds.anvilContributesMultibinding -> 2 + ClassIds.anvilContributesSubcomponent -> 4 + else -> errorWithAttachment( + "Couldn't find index of replaces argument for $annotationClassId.", + ) + } +} + +public fun FirAnnotation.requireScopeArgument(session: FirSession): ConeKotlinType { + return getKClassArgument(Names.scope, session) + ?: errorWithAttachment("Scope argument is not resolved yet: ${this@requireScopeArgument.render()}") { + withFirEntry("scope argument expression", this@requireScopeArgument) } - return evaluated.requireTargetType() } public fun FirAnnotation.requireScopeArgument( typeResolveService: FirSupertypeGenerationExtension.TypeResolveService, -): ConeKotlinType = requireScopeArgument().resolveConeType(typeResolveService) +): ConeKotlinType { + val getClass = requireScopeArgument() + val ct = getClass.resolveConeType(typeResolveService) + val typeArgs = ct.typeArguments + + return typeArgs.singleOrNull()?.type ?: ct +} public fun FirAnnotation.requireScopeArgument(): FirGetClassCall { return requireArgumentAt( @@ -186,3 +248,41 @@ public fun createFirAnnotation( coneType = type.constructClassLikeType() } } + +public fun createOptInAnnotation(optInType: ClassId, session: FirSession): FirAnnotation { + return createFirAnnotation( + type = OptInNames.OPT_IN_CLASS_ID, + argumentMapping = buildAnnotationArgumentMapping { + mapping[OptInNames.OPT_IN_ANNOTATION_CLASS] = + optInType.requireClassLikeSymbol(session).toGetClassCall() + }, + ) +} + +/** + * Creates a [FirAnnotation] instance with the specified type, argument mapping, source, and use-site target. + * + * @param type The [ClassId] representing the annotation type. + * @param containingDeclarationSymbol The symbol of the declaration to be annotated. + * @param argumentList Named parameters. Defaults to empty. + * @param source `null` or a [org.jetbrains.kotlin.KtFakeSourceElement] is typically fine. + * @param useSiteTarget `get`, `set`, `field`, `file`, etc. see [AnnotationUseSiteTarget]. + * @return A [FirAnnotation] instance. + */ +public fun createFirAnnotationCall( + type: ClassId, + containingDeclarationSymbol: FirBasedSymbol<*>, + argumentList: FirArgumentList = FirEmptyArgumentList, + source: KtSourceElement? = null, + useSiteTarget: AnnotationUseSiteTarget? = null, + ktPsiFactory: KtPsiFactory? = null, +): FirAnnotationCall = buildAnnotationCall { + setAnnotationType(type, ktPsiFactory) + this.containingDeclarationSymbol = containingDeclarationSymbol + this.argumentList = argumentList + this.source = source + this.useSiteTarget = useSiteTarget + annotationTypeRef = buildResolvedTypeRef { + coneType = type.constructClassLikeType() + } +} diff --git a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirBasedSymbolUtils.kt b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirBasedSymbolUtils.kt index 28d40a08b..12a8ac252 100644 --- a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirBasedSymbolUtils.kt +++ b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirBasedSymbolUtils.kt @@ -11,25 +11,44 @@ import org.jetbrains.kotlin.fir.utils.exceptions.withFirSymbolEntry import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment -public fun FirBasedSymbol<*>.hasAnnotation(classId: ClassId, session: FirSession): Boolean { +public fun FirBasedSymbol<*>.hasAnnotation( + session: FirSession, + classId: ClassId, +): Boolean { return annotations.any { it.classId(session) == classId } } public fun FirBasedSymbol.requireAnnotationCall( classId: ClassId, session: FirSession, + resolveArguments: Boolean, ): FirAnnotationCall where D : FirAnnotationContainer, D : FirDeclaration { - return requireAnnotation(classId, session) as FirAnnotationCall + return requireAnnotation(classId, session, resolveArguments) as FirAnnotationCall } public fun FirBasedSymbol.requireAnnotation( classId: ClassId, session: FirSession, + resolveArguments: Boolean, ): FirAnnotation where D : FirAnnotationContainer, D : FirDeclaration { - return getAnnotationByClassId(classId, session) + return getResolvedAnnotation(classId, session, resolveArguments = resolveArguments) ?: errorWithAttachment("Annotation with classId $classId not found on $this") { withFirSymbolEntry("symbol", this@requireAnnotation) } } + +public fun FirBasedSymbol.getResolvedAnnotation( + classId: ClassId, + session: FirSession, + resolveArguments: Boolean, +): FirAnnotation? where D : FirAnnotationContainer, + D : FirDeclaration { + val resolved = if (resolveArguments) { + resolvedAnnotationsWithArguments + } else { + resolvedCompilerAnnotationsWithClassIds + } + return resolved.getAnnotationByClassId(classId, session) +} diff --git a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirClassLikeSymbolUtils.kt b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirClassLikeSymbolUtils.kt index e94d934a0..90fe22456 100644 --- a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirClassLikeSymbolUtils.kt +++ b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirClassLikeSymbolUtils.kt @@ -2,6 +2,7 @@ package com.squareup.anvil.compiler.k2.utils.fir import com.squareup.anvil.compiler.k2.utils.names.ClassIds import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.getAnnotationsByClassId import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall import org.jetbrains.kotlin.fir.expressions.FirGetClassCall import org.jetbrains.kotlin.fir.expressions.buildUnaryArgumentList @@ -20,10 +21,19 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.StandardClassIds +public fun FirClassLikeSymbol<*>.getContributesBindingAnnotations( + session: FirSession, +): List { + return resolvedAnnotationsWithArguments + .filter { it.classId(session) == ClassIds.anvilContributesBinding } + .map { it as FirAnnotationCall } +} + public fun FirClassLikeSymbol<*>.contributesToAnnotations( session: FirSession, ): List { - return annotations.filter { it.classId(session) == ClassIds.anvilContributesTo } + return resolvedAnnotationsWithArguments + .getAnnotationsByClassId(ClassIds.anvilContributesTo, session) .map { it as FirAnnotationCall } } diff --git a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirFileUtils.kt b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirFileUtils.kt new file mode 100644 index 000000000..01f18d25d --- /dev/null +++ b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirFileUtils.kt @@ -0,0 +1,58 @@ +package com.squareup.anvil.compiler.k2.utils.fir + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.builder.buildPackageDirective +import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.declarations.FirFile +import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.builder.buildFile +import org.jetbrains.kotlin.fir.declarations.utils.classId +import org.jetbrains.kotlin.fir.moduleData +import org.jetbrains.kotlin.fir.packageFqName +import org.jetbrains.kotlin.fir.resolve.providers.firProvider +import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.utils.exceptions.checkWithAttachment + +public fun FirRegularClass.wrapInSyntheticFile(session: FirSession): FirRegularClass = apply { + session.createSyntheticFile( + origin = origin, + packageName = classId.packageFqName, + simpleName = "${classId.shortClassName.asString()}.kt", + declarations = listOf(this), + ) +} + +public fun FirProperty.wrapInSyntheticFile(session: FirSession): FirProperty = apply { + session.createSyntheticFile( + origin = origin, + packageName = symbol.packageFqName(), + simpleName = "${name.asString()}.kt", + declarations = listOf(this), + ) +} + +public fun FirSession.createSyntheticFile( + origin: FirDeclarationOrigin, + packageName: FqName, + simpleName: String, + declarations: List, +): FirFile = buildFile { + this.origin = origin + this@buildFile.moduleData = this@createSyntheticFile.moduleData + packageDirective = buildPackageDirective { + this.packageFqName = packageName + } + checkWithAttachment( + simpleName.matches(".+\\.kts?$".toRegex()), + { "simpleName must end with .kt or .kts" }, + ) { + withEntry("simpleName", simpleName) + } + this.name = simpleName + this.declarations.addAll(declarations) +}.also { + (firProvider as FirProviderImpl).recordFile(it) +} diff --git a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirGetClassCallUtils.kt b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirGetClassCallUtils.kt index c29f0ab3b..58e95c7ef 100644 --- a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirGetClassCallUtils.kt +++ b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirGetClassCallUtils.kt @@ -14,6 +14,11 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.utils.exceptions.checkWithAttachment import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment +/** For `kotlin.Unit::class`, returns `kotlin.Unit`. */ +public fun FirGetClassCall.requireTargetClassId( + typeResolveService: TypeResolveService, +): ClassId = resolveConeType(typeResolveService).requireClassId() + /** For `kotlin.Unit::class`, returns `kotlin.Unit`. */ public fun FirGetClassCall.requireTargetClassId(): ClassId = requireTargetType().requireClassId() diff --git a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirNamedArgumentExpressionUtils.kt b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirNamedArgumentExpressionUtils.kt new file mode 100644 index 000000000..5667315ba --- /dev/null +++ b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirNamedArgumentExpressionUtils.kt @@ -0,0 +1,23 @@ +package com.squareup.anvil.compiler.k2.utils.fir + +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression +import org.jetbrains.kotlin.fir.expressions.builder.buildArgumentList +import org.jetbrains.kotlin.fir.expressions.builder.buildArrayLiteral +import org.jetbrains.kotlin.fir.expressions.builder.buildNamedArgumentExpression +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name + +public fun createClassListArgument( + argumentName: Name, + classes: List, + session: FirSession, +): FirNamedArgumentExpression = buildNamedArgumentExpression { + this.name = argumentName + isSpread = false + expression = buildArrayLiteral { + argumentList = buildArgumentList { + arguments += classes.map { it.requireClassLikeSymbol(session).toGetClassCall() } + } + } +} diff --git a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirRegularClassSymbolUtils.kt b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirRegularClassSymbolUtils.kt new file mode 100644 index 000000000..34aeb443d --- /dev/null +++ b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/fir/FirRegularClassSymbolUtils.kt @@ -0,0 +1,36 @@ +package com.squareup.anvil.compiler.k2.utils.fir + +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.ConeLookupTagBasedType +import org.jetbrains.kotlin.fir.types.ConeTypeProjection +import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.constructClassLikeType +import org.jetbrains.kotlin.fir.types.constructType +import org.jetbrains.kotlin.fir.types.toLookupTag +import org.jetbrains.kotlin.name.StandardClassIds + +public fun FirRegularClassSymbol.resolvedTypeRef(): FirResolvedTypeRef { + return buildResolvedTypeRef { coneType = toKClassRef() } +} + +/** + * Creates the type name symbol as you would see it in a type argument, like + * `com.foo.Bar` in `List`. + */ +internal fun FirRegularClassSymbol.coneLookupTagBasedType(): ConeLookupTagBasedType { + return classId.toLookupTag().constructType( + typeArguments = emptyArray(), + isMarkedNullable = false, + ) +} + +/** + * Creates a `kotlin.reflect.KClass` reference to the class symbol, like `KClass`. + */ +internal fun FirRegularClassSymbol.toKClassRef(): ConeClassLikeType = + StandardClassIds.KClass.constructClassLikeType( + typeArguments = arrayOf(coneLookupTagBasedType()), + isMarkedNullable = false, + ) diff --git a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/names/ClassIds.kt b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/names/ClassIds.kt index 34fb76618..d9258e9e8 100644 --- a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/names/ClassIds.kt +++ b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/names/ClassIds.kt @@ -4,6 +4,7 @@ import com.squareup.anvil.annotations.ExperimentalAnvilApi import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import java.security.MessageDigest /** Well-known class ids used by Anvil. */ public object ClassIds { @@ -45,6 +46,18 @@ public object ClassIds { public val anvilContributesTo: ClassId = classId("com.squareup.anvil.annotations", "ContributesTo") + /** `com.squareup.anvil.annotations.internal.InternalContributedModuleHints` */ + public val anvilInternalContributedModuleHints: ClassId = + classId("com.squareup.anvil.annotations.internal", "InternalContributedModuleHints") + + /** `com.squareup.anvil.annotations.internal.InternalContributedComponentHints` */ + public val anvilInternalContributedComponentHints: ClassId = + classId("com.squareup.anvil.annotations.internal", "InternalContributedComponentHints") + + /** `com.squareup.anvil.annotations.internal.InternalAnvilApi` */ + public val anvilInternalAnvilApi: ClassId = + classId("com.squareup.anvil.annotations.internal", "InternalAnvilApi") + /** `com.squareup.anvil.annotations.MergeComponent` */ public val anvilMergeComponent: ClassId = classId("com.squareup.anvil.annotations", "MergeComponent") @@ -64,6 +77,9 @@ public object ClassIds { /** `dagger.Binds` */ public val daggerBinds: ClassId = classId("dagger", "Binds") + /** `dagger.Provides` */ + public val daggerProvides: ClassId = classId("dagger", "Provides") + /** `dagger.Component` */ public val daggerComponent: ClassId = classId("dagger", "Component") @@ -81,6 +97,29 @@ public object ClassIds { /** `dagger.Subcomponent` */ public val daggerSubcomponent: ClassId = classId("dagger", "Subcomponent") + + public fun anvilContributedModules(moduleTypes: List): ClassId { + return ClassId( + FqNames.anvilHintPackage, + Name.identifier("AnvilModuleHints_${md5Hash(moduleTypes.map { it.asString() })}"), + ) + } + + public fun anvilContributedComponents(componentTypes: List): ClassId { + return ClassId( + FqNames.anvilHintPackage, + Name.identifier("AnvilComponentHints_${md5Hash(componentTypes.map { it.asString() })}"), + ) + } + + private const val HASH_STRING_LENGTH: Int = 8 + + private fun md5Hash(params: List): String = + MessageDigest.getInstance("MD5") + .apply { for (it in params) update(it.toString().toByteArray()) } + .digest() + .take(HASH_STRING_LENGTH / 2) + .joinToString("") { "%02x".format(it) } } private fun classId( diff --git a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/names/FqNames.kt b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/names/FqNames.kt new file mode 100644 index 000000000..3f7dee7d3 --- /dev/null +++ b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/names/FqNames.kt @@ -0,0 +1,8 @@ +package com.squareup.anvil.compiler.k2.utils.names + +import org.jetbrains.kotlin.name.FqName + +public object FqNames { + /** `anvil.hint` */ + public val anvilHintPackage: FqName = FqName("anvil.hint") +} diff --git a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/names/Names.kt b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/names/Names.kt index 8550d5300..da4c27546 100644 --- a/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/names/Names.kt +++ b/compiler-k2-utils/src/main/kotlin/com/squareup/anvil/compiler/k2/utils/names/Names.kt @@ -3,7 +3,12 @@ package com.squareup.anvil.compiler.k2.utils.names import org.jetbrains.kotlin.name.Name public object Names { + public val boundType: Name = Name.identifier("boundType") public val dependencies: Name = Name.identifier("dependencies") + public val exclude: Name = Name.identifier("exclude") + public val hints: Name = Name.identifier("hints") public val modules: Name = Name.identifier("modules") + public val rank: Name = Name.identifier("rank") + public val replaces: Name = Name.identifier("replaces") public val scope: Name = Name.identifier("scope") } diff --git a/compiler-k2/api/compiler-k2.api b/compiler-k2/api/compiler-k2.api index e0dfed2bb..068617a50 100644 --- a/compiler-k2/api/compiler-k2.api +++ b/compiler-k2/api/compiler-k2.api @@ -8,60 +8,155 @@ public final class com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionRegistrar public fun (Lorg/jetbrains/kotlin/cli/common/messages/MessageCollector;)V } -public final class com/squareup/anvil/compiler/k2/fir/contributions/BindingModuleData { - public fun (Lorg/jetbrains/kotlin/name/ClassId;Lorg/jetbrains/kotlin/fir/symbols/impl/FirClassSymbol;Lorg/jetbrains/kotlin/fir/extensions/FirExtension;Lorg/jetbrains/kotlin/fir/FirSession;)V - public final fun getBoundType ()Lorg/jetbrains/kotlin/fir/types/ConeKotlinType; - public final fun getCallableName ()Lorg/jetbrains/kotlin/name/Name; - public final fun getContributesBindingAnnotation ()Lorg/jetbrains/kotlin/fir/expressions/FirAnnotation; - public final fun getGeneratedClassId ()Lorg/jetbrains/kotlin/name/ClassId; - public final fun getGeneratedClassSymbol ()Lorg/jetbrains/kotlin/fir/symbols/impl/FirClassLikeSymbol; - public final fun getMatchedClassSymbol ()Lorg/jetbrains/kotlin/fir/symbols/impl/FirClassSymbol; +public final class com/squareup/anvil/compiler/k2/fir/abstraction/AnvilContributedComponentHintGeneratorFactory : com/squareup/anvil/compiler/k2/fir/AbstractAnvilFirProcessorFactory { + public fun ()V } -public final class com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingFirExtension : com/squareup/anvil/compiler/k2/fir/AnvilFirDeclarationGenerationExtension { - public fun (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;Lorg/jetbrains/kotlin/fir/FirSession;)V +public final class com/squareup/anvil/compiler/k2/fir/abstraction/AnvilContributedModuleHintGeneratorFactory : com/squareup/anvil/compiler/k2/fir/AbstractAnvilFirProcessorFactory { + public fun ()V +} + +public final class com/squareup/anvil/compiler/k2/fir/abstraction/BindingModuleGeneratorFactory : com/squareup/anvil/compiler/k2/fir/AbstractAnvilFirProcessorFactory { + public fun ()V +} + +public final class com/squareup/anvil/compiler/k2/fir/abstraction/extensions/SupertypeProcessorExtension : org/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension { + public fun (Lorg/jetbrains/kotlin/fir/FirSession;)V + public fun computeAdditionalSupertypes (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;Ljava/util/List;Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$TypeResolveService;)Ljava/util/List; + public fun computeAdditionalSupertypesForGeneratedNestedClass (Lorg/jetbrains/kotlin/fir/declarations/FirRegularClass;Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$TypeResolveService;)Ljava/util/List; + public fun needTransformSupertypes (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;)Z + public fun registerPredicates (Lorg/jetbrains/kotlin/fir/extensions/FirDeclarationPredicateRegistrar;)V +} + +public final class com/squareup/anvil/compiler/k2/fir/abstraction/extensions/TopLevelClassProcessorExtension : org/jetbrains/kotlin/fir/extensions/FirDeclarationGenerationExtension { + public fun (Lorg/jetbrains/kotlin/fir/FirSession;)V public fun generateFunctions (Lorg/jetbrains/kotlin/name/CallableId;Lorg/jetbrains/kotlin/fir/extensions/DeclarationGenerationContext$Member;)Ljava/util/List; public fun generateTopLevelClassLikeDeclaration (Lorg/jetbrains/kotlin/name/ClassId;)Lorg/jetbrains/kotlin/fir/symbols/impl/FirClassLikeSymbol; public fun getCallableNamesForClass (Lorg/jetbrains/kotlin/fir/symbols/impl/FirClassSymbol;Lorg/jetbrains/kotlin/fir/extensions/DeclarationGenerationContext$Member;)Ljava/util/Set; public fun getTopLevelClassIds ()Ljava/util/Set; + public fun hasPackage (Lorg/jetbrains/kotlin/name/FqName;)Z public fun registerPredicates (Lorg/jetbrains/kotlin/fir/extensions/FirDeclarationPredicateRegistrar;)V } -public final class com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingSessionComponent : com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionSessionComponent { +public final class com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirDependencyHintProvider : com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionSessionComponent { public fun (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;Lorg/jetbrains/kotlin/fir/FirSession;)V - public final fun getBindingModuleCache ()Lorg/jetbrains/kotlin/fir/caches/FirCache; - public final fun getGeneratedIdsToMatchedSymbols ()Ljava/util/Map; + public final fun getAllDependencyContributedComponents ()Ljava/util/List; + public final fun getAllDependencyContributedModules ()Ljava/util/List; public fun registerPredicates (Lorg/jetbrains/kotlin/fir/extensions/FirDeclarationPredicateRegistrar;)V } -public final class com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingSessionComponentKt { - public static final fun getContributesBindingSessionComponent (Lorg/jetbrains/kotlin/fir/FirSession;)Lcom/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingSessionComponent; +public final class com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirDependencyHintProviderFactory : com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionSessionComponent$Factory { + public fun ()V + public synthetic fun create (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;)Lorg/jetbrains/kotlin/fir/extensions/FirExtension$Factory; + public fun create (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;)Lorg/jetbrains/kotlin/fir/extensions/FirExtensionSessionComponent$Factory; +} + +public final class com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirDependencyHintProviderKt { + public static final fun getAnvilFirDependencyHintProvider (Lorg/jetbrains/kotlin/fir/FirSession;)Lcom/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirDependencyHintProvider; +} + +public final class com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirProcessorProvider : org/jetbrains/kotlin/fir/extensions/FirExtensionSessionComponent { + public fun (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext2;)V + public final fun getFlushingSupertypeProcessors ()Ljava/util/List; + public final fun getSupertypeProcessors ()Ljava/util/List; + public final fun getTopLevelClassProcessors ()Ljava/util/List; } -public final class com/squareup/anvil/compiler/k2/fir/merging/AnvilFirAnnotationMergingExtension : com/squareup/anvil/compiler/k2/fir/AnvilFirSupertypeGenerationExtension { +public final class com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirProcessorProviderKt { + public static final fun getAnvilFirProcessorProvider (Lorg/jetbrains/kotlin/fir/FirSession;)Lcom/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirProcessorProvider; +} + +public final class com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirSymbolProvider : com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionSessionComponent { public fun (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;Lorg/jetbrains/kotlin/fir/FirSession;)V - public fun computeAdditionalSupertypes (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;Ljava/util/List;Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$TypeResolveService;)Ljava/util/List; - public fun needTransformSupertypes (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;)Z public fun registerPredicates (Lorg/jetbrains/kotlin/fir/extensions/FirDeclarationPredicateRegistrar;)V } -public final class com/squareup/anvil/compiler/k2/fir/merging/AnvilFirAnnotationMergingExtension$Factory : com/squareup/anvil/compiler/k2/fir/AnvilFirSupertypeGenerationExtension$Factory { +public final class com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirSymbolProviderFactory : com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionSessionComponent$Factory { public fun ()V public synthetic fun create (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;)Lorg/jetbrains/kotlin/fir/extensions/FirExtension$Factory; - public fun create (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;)Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$Factory; + public fun create (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;)Lorg/jetbrains/kotlin/fir/extensions/FirExtensionSessionComponent$Factory; +} + +public final class com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirSymbolProviderKt { + public static final fun getAnvilFirSymbolProvider (Lorg/jetbrains/kotlin/fir/FirSession;)Lcom/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirSymbolProvider; } -public final class com/squareup/anvil/compiler/k2/fir/merging/AnvilFirInterfaceMergingExtension : com/squareup/anvil/compiler/k2/fir/AnvilFirSupertypeGenerationExtension { +public final class com/squareup/anvil/compiler/k2/fir/abstraction/providers/ScopedContributionProvider : com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionSessionComponent { public fun (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;Lorg/jetbrains/kotlin/fir/FirSession;)V - public fun computeAdditionalSupertypes (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;Ljava/util/List;Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$TypeResolveService;)Ljava/util/List; - public fun needTransformSupertypes (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;)Z - public fun registerPredicates (Lorg/jetbrains/kotlin/fir/extensions/FirDeclarationPredicateRegistrar;)V + public final fun getContributedBindingModules ()Ljava/util/List; + public final fun getContributedBindings ()Ljava/util/List; + public final fun getContributedModules ()Ljava/util/List; + public final fun getContributedSupertypes ()Ljava/util/List; + public final fun getMergedComponents ()Ljava/util/List; } -public final class com/squareup/anvil/compiler/k2/fir/merging/AnvilFirInterfaceMergingExtension$Factory : com/squareup/anvil/compiler/k2/fir/AnvilFirSupertypeGenerationExtension$Factory { +public final class com/squareup/anvil/compiler/k2/fir/abstraction/providers/ScopedContributionProviderFactory : com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionSessionComponent$Factory { public fun ()V public synthetic fun create (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;)Lorg/jetbrains/kotlin/fir/extensions/FirExtension$Factory; - public fun create (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;)Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$Factory; + public fun create (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext;)Lorg/jetbrains/kotlin/fir/extensions/FirExtensionSessionComponent$Factory; +} + +public final class com/squareup/anvil/compiler/k2/fir/abstraction/providers/ScopedContributionProviderKt { + public static final fun getScopedContributionProvider (Lorg/jetbrains/kotlin/fir/FirSession;)Lcom/squareup/anvil/compiler/k2/fir/abstraction/providers/ScopedContributionProvider; +} + +public abstract interface class com/squareup/anvil/compiler/k2/fir/internal/AnvilLogger { + public abstract fun error (Ljava/lang/String;)V + public abstract fun exception (Ljava/lang/Throwable;)V + public abstract fun getErrorWriter ()Ljava/io/PrintWriter; + public abstract fun getInfoWriter ()Ljava/io/PrintWriter; + public abstract fun getWarnWriter ()Ljava/io/PrintWriter; + public abstract fun info (Ljava/lang/String;)V + public abstract fun isVerbose ()Z + public abstract fun warn (Ljava/lang/String;)V +} + +public final class com/squareup/anvil/compiler/k2/fir/internal/AnvilLoggerKt { + public static final fun info (Lcom/squareup/anvil/compiler/k2/fir/internal/AnvilLogger;Lkotlin/jvm/functions/Function0;)V +} + +public final class com/squareup/anvil/compiler/k2/fir/internal/MessageCollectorBackedAnvilLogger : com/squareup/anvil/compiler/k2/fir/internal/AnvilLogger { + public static final field PREFIX Ljava/lang/String; + public fun (ZZLorg/jetbrains/kotlin/cli/common/messages/MessageCollector;)V + public synthetic fun (ZZLorg/jetbrains/kotlin/cli/common/messages/MessageCollector;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun error (Ljava/lang/String;)V + public fun exception (Ljava/lang/Throwable;)V + public fun getErrorWriter ()Ljava/io/PrintWriter; + public fun getInfoWriter ()Ljava/io/PrintWriter; + public final fun getMessageCollector ()Lorg/jetbrains/kotlin/cli/common/messages/MessageCollector; + public fun getWarnWriter ()Ljava/io/PrintWriter; + public fun info (Ljava/lang/String;)V + public fun isVerbose ()Z + public fun warn (Ljava/lang/String;)V +} + +public final class com/squareup/anvil/compiler/k2/fir/internal/MessageCollectorBackedWriter : java/io/Writer { + public fun (Lorg/jetbrains/kotlin/cli/common/messages/MessageCollector;Lorg/jetbrains/kotlin/cli/common/messages/CompilerMessageSeverity;)V + public fun close ()V + public fun flush ()V + public fun write ([CII)V +} + +public final class com/squareup/anvil/compiler/k2/fir/merging/AnnotationMergingGenerator : com/squareup/anvil/compiler/k2/fir/FlushingSupertypeProcessor { + public fun (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext2;)V + public fun generateAnnotation (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;)Lorg/jetbrains/kotlin/fir/expressions/FirAnnotationCall; + public fun getAnvilContext ()Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext2; + public fun shouldProcess (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;)Z +} + +public final class com/squareup/anvil/compiler/k2/fir/merging/AnnotationMergingGeneratorFactory : com/squareup/anvil/compiler/k2/fir/AbstractAnvilFirProcessorFactory { + public fun ()V +} + +public final class com/squareup/anvil/compiler/k2/fir/merging/InterfaceMergingGenerator : com/squareup/anvil/compiler/k2/fir/SupertypeProcessor { + public fun (Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext2;)V + public fun addSupertypes (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;Ljava/util/List;Lorg/jetbrains/kotlin/fir/extensions/FirSupertypeGenerationExtension$TypeResolveService;)Ljava/util/List; + public fun getAnvilContext ()Lcom/squareup/anvil/compiler/k2/fir/AnvilFirContext2; + public fun shouldProcess (Lorg/jetbrains/kotlin/fir/declarations/FirClassLikeDeclaration;)Z +} + +public final class com/squareup/anvil/compiler/k2/fir/merging/InterfaceMergingGeneratorFactory : com/squareup/anvil/compiler/k2/fir/AbstractAnvilFirProcessorFactory { + public fun ()V } public final class com/squareup/anvil/compiler/k2/ir/GeneratedDeclarationsIrBodyFiller : org/jetbrains/kotlin/backend/common/extensions/IrGenerationExtension { @@ -69,4 +164,3 @@ public final class com/squareup/anvil/compiler/k2/ir/GeneratedDeclarationsIrBody public fun generate (Lorg/jetbrains/kotlin/ir/declarations/IrModuleFragment;Lorg/jetbrains/kotlin/backend/common/extensions/IrPluginContext;)V public fun resolveSymbol (Lorg/jetbrains/kotlin/ir/symbols/IrSymbol;Lorg/jetbrains/kotlin/ir/builders/TranslationPluginContext;)Lorg/jetbrains/kotlin/ir/declarations/IrDeclaration; } - diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/constructor/inject/FirInjectConstructorFactoryGenerationExtension.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/constructor/inject/FirInjectConstructorFactoryGenerationExtension.kt index c22999db4..8f3d3c440 100644 --- a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/constructor/inject/FirInjectConstructorFactoryGenerationExtension.kt +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/constructor/inject/FirInjectConstructorFactoryGenerationExtension.kt @@ -1,13 +1,12 @@ package com.squareup.anvil.compiler.k2.constructor.inject -import com.squareup.anvil.compiler.k2.fir.AnvilFirContext -import com.squareup.anvil.compiler.k2.fir.AnvilFirDeclarationGenerationExtension import com.squareup.anvil.compiler.k2.utils.names.ClassIds import org.jetbrains.kotlin.GeneratedDeclarationKey import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.utils.isCompanion import org.jetbrains.kotlin.fir.expressions.builder.buildPropertyAccessExpression import org.jetbrains.kotlin.fir.extensions.ExperimentalTopLevelDeclarationsGenerationApi +import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext import org.jetbrains.kotlin.fir.extensions.NestedClassGenerationContext @@ -24,7 +23,6 @@ import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames -import kotlin.collections.get /** * Given this kotlin source: @@ -49,10 +47,8 @@ import kotlin.collections.get * } * } */ -internal class FirInjectConstructorFactoryGenerationExtension( - anvilFirContext: AnvilFirContext, - session: FirSession, -) : AnvilFirDeclarationGenerationExtension(anvilFirContext, session) { +internal class FirInjectConstructorFactoryGenerationExtension(session: FirSession) : + FirDeclarationGenerationExtension(session) { private val factoriesToGenerate: Map by lazy { session.predicateBasedProvider.getSymbolsByPredicate(injectAnnotationPredicate) @@ -117,7 +113,7 @@ internal class FirInjectConstructorFactoryGenerationExtension( ): List { val owner = context?.owner ?: return emptyList() val param = factoriesToGenerate[owner.classId] - ?.generatedCallableIdToParameters[callableId] + ?.generatedCallableIdToParameters?.get(callableId) ?: return emptyList() return createMemberProperty( diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/constructor/inject/FirInjectConstructorFactoryGenerationExtensionFactory.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/constructor/inject/FirInjectConstructorFactoryGenerationExtensionFactory.kt deleted file mode 100644 index 548b0d290..000000000 --- a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/constructor/inject/FirInjectConstructorFactoryGenerationExtensionFactory.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.squareup.anvil.compiler.k2.constructor.inject - -import com.google.auto.service.AutoService -import com.squareup.anvil.compiler.k2.fir.AnvilFirContext -import com.squareup.anvil.compiler.k2.fir.AnvilFirDeclarationGenerationExtension -import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionFactory -import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension - -@AutoService(AnvilFirExtensionFactory::class) -internal class FirInjectConstructorFactoryGenerationExtensionFactory : - AnvilFirDeclarationGenerationExtension.Factory { - override fun create( - anvilFirContext: AnvilFirContext, - ): FirDeclarationGenerationExtension.Factory = FirDeclarationGenerationExtension.Factory { - FirInjectConstructorFactoryGenerationExtension( - anvilFirContext = anvilFirContext, - session = it, - ) - } -} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionRegistrar.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionRegistrar.kt index 7d1b3ba1e..4f8b4730b 100644 --- a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionRegistrar.kt +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/AnvilFirExtensionRegistrar.kt @@ -1,7 +1,15 @@ package com.squareup.anvil.compiler.k2.fir +import com.squareup.anvil.compiler.k2.constructor.inject.FirInjectConstructorFactoryGenerationExtension +import com.squareup.anvil.compiler.k2.fir.abstraction.extensions.SupertypeProcessorExtension +import com.squareup.anvil.compiler.k2.fir.abstraction.extensions.TopLevelClassProcessorExtension +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.AnvilFirDependencyHintProvider +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.AnvilFirProcessorProvider +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.AnvilFirSymbolProvider +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.ScopedContributionProvider import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar +import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent import java.util.ServiceLoader public class AnvilFirExtensionRegistrar( @@ -10,20 +18,22 @@ public class AnvilFirExtensionRegistrar( override fun ExtensionRegistrarContext.configurePlugin() { - val context = AnvilFirContext(messageCollector) + +FirExtensionSessionComponent.Factory { AnvilFirContext(messageCollector, it) } - val factories = ServiceLoader.load( - AnvilFirExtensionFactory::class.java, - javaClass.classLoader, - ) + +::SupertypeProcessorExtension + +::TopLevelClassProcessorExtension - for (factory in factories) { + +::AnvilFirDependencyHintProvider + +::AnvilFirProcessorProvider + +::AnvilFirSymbolProvider + +::ScopedContributionProvider + + +::FirInjectConstructorFactoryGenerationExtension - when (factory) { - is AnvilFirDeclarationGenerationExtension.Factory -> factory.create(context).unaryPlus() - is AnvilFirSupertypeGenerationExtension.Factory -> factory.create(context).unaryPlus() - is AnvilFirExtensionSessionComponent.Factory -> factory.create(context).unaryPlus() - } + val factories = ServiceLoader.load(FirExtensionSessionComponent.Factory::class.java) + + for (factory in factories) { + +factory } } } diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/AnvilContributedComponentHintGenerator.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/AnvilContributedComponentHintGenerator.kt new file mode 100644 index 000000000..fc51fdbb6 --- /dev/null +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/AnvilContributedComponentHintGenerator.kt @@ -0,0 +1,106 @@ +package com.squareup.anvil.compiler.k2.fir.abstraction + +import com.google.auto.service.AutoService +import com.squareup.anvil.compiler.k2.fir.AbstractAnvilFirProcessorFactory +import com.squareup.anvil.compiler.k2.fir.AnvilFirContext +import com.squareup.anvil.compiler.k2.fir.AnvilFirProcessor +import com.squareup.anvil.compiler.k2.fir.PendingTopLevelClass +import com.squareup.anvil.compiler.k2.fir.RequiresTypesResolutionPhase +import com.squareup.anvil.compiler.k2.fir.TopLevelClassProcessor +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.anvilFirSymbolProvider +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.scopedContributionProvider +import com.squareup.anvil.compiler.k2.fir.contributions.GeneratedBindingHintKey +import com.squareup.anvil.compiler.k2.utils.fir.createFirAnnotation +import com.squareup.anvil.compiler.k2.utils.fir.createOptInAnnotation +import com.squareup.anvil.compiler.k2.utils.names.ClassIds +import com.squareup.anvil.compiler.k2.utils.names.FqNames +import com.squareup.anvil.compiler.k2.utils.names.Names +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationArgumentMapping +import org.jetbrains.kotlin.fir.expressions.builder.buildArgumentList +import org.jetbrains.kotlin.fir.expressions.builder.buildArrayLiteral +import org.jetbrains.kotlin.fir.expressions.builder.buildLiteralExpression +import org.jetbrains.kotlin.fir.extensions.ExperimentalTopLevelDeclarationsGenerationApi +import org.jetbrains.kotlin.fir.extensions.FirExtension +import org.jetbrains.kotlin.fir.types.createArrayType +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.types.ConstantValueKind + +@AutoService(AnvilFirProcessor.Factory::class) +public class AnvilContributedComponentHintGeneratorFactory : + AbstractAnvilFirProcessorFactory(::AnvilContributedComponentHintGenerator) + +internal class AnvilContributedComponentHintGenerator( + override val anvilContext: AnvilFirContext, +) : TopLevelClassProcessor() { + + @OptIn(RequiresTypesResolutionPhase::class) + private val contributedSupertypes by lazyValue { + session.scopedContributionProvider.contributedSupertypes + } + + override fun hasPackage(packageFqName: FqName): Boolean { + return packageFqName == FqNames.anvilHintPackage && + session.anvilFirSymbolProvider.contributesSupertypeSymbols.isNotEmpty() + } + + @ExperimentalTopLevelDeclarationsGenerationApi + override fun getTopLevelClassIds(): Set { + return if (contributedSupertypes.isEmpty()) { + emptySet() + } else { + setOf(ClassIds.anvilContributedComponents(contributedSupertypes.map { it.contributedType })) + } + } + + @ExperimentalTopLevelDeclarationsGenerationApi + override fun generateTopLevelClassLikeDeclaration( + classId: ClassId, + firExtension: FirExtension, + ): PendingTopLevelClass = PendingTopLevelClass( + classId = classId, + key = GeneratedBindingHintKey, + classKind = ClassKind.INTERFACE, + visibility = Visibilities.Private, + annotations = lazyValue { + val sortedHints = contributedSupertypes.map { component -> + listOf( + component.scopeType.getValue(), + component.contributedType, + *component.replaces.getValue().toTypedArray(), + ).joinToString("|") { it.asFqNameString() } + } + .sorted() + + listOf( + createOptInAnnotation(ClassIds.anvilInternalAnvilApi, session), + createFirAnnotation( + type = ClassIds.anvilInternalContributedComponentHints, + argumentMapping = buildAnnotationArgumentMapping { + mapping[Names.hints] = buildArrayLiteral { + coneTypeOrNull = session.builtinTypes.stringType.coneType.createArrayType() + argumentList = buildArgumentList { + arguments += sortedHints.map { hint -> + buildLiteralExpression( + source = null, + kind = ConstantValueKind.String, + value = hint, + annotations = null, + setType = true, + prefix = null, + ) + } + } + } + }, + ), + ) + }, + cachesFactory = session.firCachesFactory, + firExtension = firExtension, + ) +} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/AnvilContributedModuleHintGenerator.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/AnvilContributedModuleHintGenerator.kt new file mode 100644 index 000000000..d9c3eacd8 --- /dev/null +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/AnvilContributedModuleHintGenerator.kt @@ -0,0 +1,118 @@ +package com.squareup.anvil.compiler.k2.fir.abstraction + +import com.google.auto.service.AutoService +import com.squareup.anvil.compiler.k2.fir.AbstractAnvilFirProcessorFactory +import com.squareup.anvil.compiler.k2.fir.AnvilFirContext +import com.squareup.anvil.compiler.k2.fir.AnvilFirProcessor +import com.squareup.anvil.compiler.k2.fir.PendingTopLevelClass +import com.squareup.anvil.compiler.k2.fir.RequiresTypesResolutionPhase +import com.squareup.anvil.compiler.k2.fir.TopLevelClassProcessor +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.anvilFirSymbolProvider +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.scopedContributionProvider +import com.squareup.anvil.compiler.k2.fir.contributions.GeneratedBindingHintKey +import com.squareup.anvil.compiler.k2.utils.fir.createFirAnnotation +import com.squareup.anvil.compiler.k2.utils.fir.createOptInAnnotation +import com.squareup.anvil.compiler.k2.utils.names.ClassIds +import com.squareup.anvil.compiler.k2.utils.names.FqNames +import com.squareup.anvil.compiler.k2.utils.names.Names +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationArgumentMapping +import org.jetbrains.kotlin.fir.expressions.builder.buildArgumentList +import org.jetbrains.kotlin.fir.expressions.builder.buildArrayLiteral +import org.jetbrains.kotlin.fir.expressions.builder.buildLiteralExpression +import org.jetbrains.kotlin.fir.extensions.ExperimentalTopLevelDeclarationsGenerationApi +import org.jetbrains.kotlin.fir.extensions.FirExtension +import org.jetbrains.kotlin.fir.types.createArrayType +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.types.ConstantValueKind + +@AutoService(AnvilFirProcessor.Factory::class) +public class AnvilContributedModuleHintGeneratorFactory : + AbstractAnvilFirProcessorFactory(::AnvilContributedModuleHintGenerator) + +internal class AnvilContributedModuleHintGenerator( + override val anvilContext: AnvilFirContext, +) : TopLevelClassProcessor() { + + private val contributedModuleSymbols by lazyValue { + session.anvilFirSymbolProvider.contributesModulesSymbols + } + + @OptIn(RequiresTypesResolutionPhase::class) + private val contributedModules by lazyValue { + session.scopedContributionProvider.contributedModules + session.scopedContributionProvider.contributedBindingModules + } + + private val hintClassIdsToContributedModules by lazyValue { + contributedModules.groupBy { it.contributedType }.entries + .associate { (_, modules) -> + + val hintClassId = ClassIds.anvilContributedModules(modules.map { it.contributedType }) + + hintClassId to modules + } + } + + override fun hasPackage(packageFqName: FqName): Boolean { + return packageFqName == FqNames.anvilHintPackage && contributedModuleSymbols.isNotEmpty() + } + + @ExperimentalTopLevelDeclarationsGenerationApi + override fun getTopLevelClassIds(): Set = hintClassIdsToContributedModules.keys + + @ExperimentalTopLevelDeclarationsGenerationApi + override fun generateTopLevelClassLikeDeclaration( + classId: ClassId, + firExtension: FirExtension, + ): PendingTopLevelClass { + return hintClassIdsToContributedModules.getValue(classId).let { modules -> + PendingTopLevelClass( + classId = classId, + key = GeneratedBindingHintKey, + classKind = ClassKind.INTERFACE, + visibility = Visibilities.Private, + annotations = session.firCachesFactory.createLazyValue { + + val sortedHints = modules.map { module -> + listOf( + module.scopeType.getValue(), + module.contributedType, + *module.replaces.getValue().toTypedArray(), + ).joinToString("|") { it.asFqNameString() } + } + .sorted() + + listOf( + createOptInAnnotation(ClassIds.anvilInternalAnvilApi, session), + createFirAnnotation( + type = ClassIds.anvilInternalContributedModuleHints, + argumentMapping = buildAnnotationArgumentMapping { + mapping[Names.hints] = buildArrayLiteral { + coneTypeOrNull = session.builtinTypes.stringType.coneType.createArrayType() + argumentList = buildArgumentList { + sortedHints.mapTo(arguments) { hint -> + buildLiteralExpression( + source = null, + kind = ConstantValueKind.String, + value = hint, + annotations = null, + setType = true, + prefix = null, + ) + } + } + } + }, + ), + ) + }, + cachesFactory = session.firCachesFactory, + firExtension = firExtension, + ) + } + } +} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/BindingModuleGenerator.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/BindingModuleGenerator.kt new file mode 100644 index 000000000..5a78bd92f --- /dev/null +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/BindingModuleGenerator.kt @@ -0,0 +1,106 @@ +package com.squareup.anvil.compiler.k2.fir.abstraction + +import com.google.auto.service.AutoService +import com.squareup.anvil.compiler.k2.fir.AbstractAnvilFirProcessorFactory +import com.squareup.anvil.compiler.k2.fir.AnvilFirContext +import com.squareup.anvil.compiler.k2.fir.AnvilFirProcessor +import com.squareup.anvil.compiler.k2.fir.PendingTopLevelClass +import com.squareup.anvil.compiler.k2.fir.RequiresTypesResolutionPhase +import com.squareup.anvil.compiler.k2.fir.TopLevelClassProcessor +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.anvilFirSymbolProvider +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.scopedContributionProvider +import com.squareup.anvil.compiler.k2.fir.contributions.GeneratedBindingDeclarationKey +import com.squareup.anvil.compiler.k2.utils.fir.createFirAnnotation +import com.squareup.anvil.compiler.k2.utils.names.ClassIds +import com.squareup.anvil.compiler.k2.utils.names.FqNames +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.extensions.ExperimentalTopLevelDeclarationsGenerationApi +import org.jetbrains.kotlin.fir.extensions.FirExtension +import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext +import org.jetbrains.kotlin.fir.plugin.createConeType +import org.jetbrains.kotlin.fir.plugin.createMemberFunction +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name + +@AutoService(AnvilFirProcessor.Factory::class) +public class BindingModuleGeneratorFactory : + AbstractAnvilFirProcessorFactory(::BindingModuleGenerator) + +internal class BindingModuleGenerator( + override val anvilContext: AnvilFirContext, +) : TopLevelClassProcessor() { + + @OptIn(RequiresTypesResolutionPhase::class) + private val contributedBindingsByModuleId by lazyValue { + session.scopedContributionProvider.contributedBindings.associateBy { it.bindingModule } + } + + override fun hasPackage(packageFqName: FqName): Boolean { + return packageFqName == FqNames.anvilHintPackage && + session.anvilFirSymbolProvider.contributesBindingSymbols.isNotEmpty() + } + + @ExperimentalTopLevelDeclarationsGenerationApi + override fun getTopLevelClassIds(): Set { + return contributedBindingsByModuleId.keys + } + + @ExperimentalTopLevelDeclarationsGenerationApi + override fun generateTopLevelClassLikeDeclaration( + classId: ClassId, + firExtension: FirExtension, + ): PendingTopLevelClass = contributedBindingsByModuleId.getValue(classId).let { binding -> + PendingTopLevelClass( + classId = classId, + key = GeneratedBindingDeclarationKey, + classKind = ClassKind.INTERFACE, + visibility = Visibilities.Public, + annotations = lazyValue { listOf(createFirAnnotation(ClassIds.daggerModule)) }, + cachesFactory = cachesFactory, + firExtension = firExtension, + ) + } + + override fun getCallableNamesForClass( + classSymbol: FirClassLikeSymbol<*>, + context: MemberGenerationContext, + ): Set { + val binding = contributedBindingsByModuleId[classSymbol.classId] ?: return emptySet() + + return setOf(binding.bindingCallableName) + } + + override fun generateFunctions( + callableId: CallableId, + context: MemberGenerationContext?, + firExtension: FirExtension, + ): List { + + val binding = contributedBindingsByModuleId[callableId.classId!!] ?: return emptyList() + + return listOf( + firExtension.createMemberFunction( + owner = context!!.owner, + key = GeneratedBindingDeclarationKey, + name = callableId.callableName, + returnType = binding.boundType.getValue().createConeType(session, nullable = false), + ) { + modality = Modality.ABSTRACT + valueParameter( + name = Name.identifier("concreteType"), + type = binding.contributedType.createConeType(session, nullable = false), + ) + }.apply { + replaceAnnotations(listOf(createFirAnnotation(ClassIds.daggerBinds))) + } + .symbol, + ) + } +} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/extensions/SupertypeProcessorExtension.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/extensions/SupertypeProcessorExtension.kt new file mode 100644 index 000000000..4925cbfd9 --- /dev/null +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/extensions/SupertypeProcessorExtension.kt @@ -0,0 +1,67 @@ +package com.squareup.anvil.compiler.k2.fir.abstraction.extensions + +import com.squareup.anvil.compiler.k2.fir.RequiresTypesResolutionPhase +import com.squareup.anvil.compiler.k2.fir.SupertypeProcessor +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.anvilFirProcessorProvider +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.scopedContributionProvider +import com.squareup.anvil.compiler.k2.utils.fir.AnvilPredicates +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration +import org.jetbrains.kotlin.fir.declarations.FirRegularClass +import org.jetbrains.kotlin.fir.declarations.utils.classId +import org.jetbrains.kotlin.fir.extensions.ExperimentalSupertypesGenerationApi +import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar +import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef +import org.jetbrains.kotlin.name.ClassId + +public class SupertypeProcessorExtension( + session: FirSession, +) : FirSupertypeGenerationExtension(session) { + + private val generatorsByClassId = mutableMapOf>() + + @OptIn(RequiresTypesResolutionPhase::class) + override fun needTransformSupertypes(declaration: FirClassLikeDeclaration): Boolean { + + val processors = session.anvilFirProcessorProvider.supertypeProcessors + + session.anvilFirProcessorProvider.flushingSupertypeProcessors + + val todo = processors.filter { it.shouldProcess(declaration) } + + if (todo.isNotEmpty()) { + generatorsByClassId[declaration.classId] = todo + } + + return todo.isNotEmpty() || !session.scopedContributionProvider.isInitialized() + } + + @OptIn(RequiresTypesResolutionPhase::class) + override fun computeAdditionalSupertypes( + classLikeDeclaration: FirClassLikeDeclaration, + resolvedSupertypes: List, + typeResolver: TypeResolveService, + ): List { + + session.scopedContributionProvider.bindTypeResolveService(typeResolver) + + return generatorsByClassId.remove(classLikeDeclaration.classId) + ?.flatMap { it.addSupertypes(classLikeDeclaration, resolvedSupertypes, typeResolver) } + ?: emptyList() + } + + @ExperimentalSupertypesGenerationApi + override fun computeAdditionalSupertypesForGeneratedNestedClass( + klass: FirRegularClass, + typeResolver: TypeResolveService, + ): List { + return generatorsByClassId.remove(klass.classId) + ?.flatMap { it.computeAdditionalSupertypesForGeneratedNestedClass(klass, typeResolver) } + ?: emptyList() + } + + override fun FirDeclarationPredicateRegistrar.registerPredicates() { + register(AnvilPredicates.hasAnyAnvilContributes) + } +} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/extensions/TopLevelClassProcessorExtension.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/extensions/TopLevelClassProcessorExtension.kt new file mode 100644 index 000000000..0f41357e8 --- /dev/null +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/extensions/TopLevelClassProcessorExtension.kt @@ -0,0 +1,78 @@ +package com.squareup.anvil.compiler.k2.fir.abstraction.extensions + +import com.squareup.anvil.compiler.k2.fir.TopLevelClassProcessor +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.anvilFirProcessorProvider +import com.squareup.anvil.compiler.k2.utils.fir.AnvilPredicates +import com.squareup.anvil.compiler.k2.utils.fir.wrapInSyntheticFile +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.extensions.ExperimentalTopLevelDeclarationsGenerationApi +import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension +import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar +import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name + +public class TopLevelClassProcessorExtension(session: FirSession) : + FirDeclarationGenerationExtension(session) { + + private val topLevelByClassId = mutableMapOf() + + private val generators by session.firCachesFactory.createLazyValue { + session.anvilFirProcessorProvider.topLevelClassProcessors + } + + override fun FirDeclarationPredicateRegistrar.registerPredicates() { + register(AnvilPredicates.hasAnyAnvilContributes) + } + + override fun hasPackage(packageFqName: FqName): Boolean { + return generators.any { it.hasPackage(packageFqName) } + } + + @ExperimentalTopLevelDeclarationsGenerationApi + override fun getTopLevelClassIds(): Set { + + for (generator in generators) { + for (id in generator.getTopLevelClassIds()) { + topLevelByClassId[id] = generator + } + } + + return topLevelByClassId.keys + } + + @ExperimentalTopLevelDeclarationsGenerationApi + override fun generateTopLevelClassLikeDeclaration( + classId: ClassId, + ): FirClassLikeSymbol<*>? { + return topLevelByClassId[classId] + ?.generateTopLevelClassLikeDeclaration(classId, firExtension = this) + ?.generatedClass + ?.getValue() + ?.wrapInSyntheticFile(session) + ?.symbol + } + + override fun getCallableNamesForClass( + classSymbol: FirClassSymbol<*>, + context: MemberGenerationContext, + ): Set = topLevelByClassId[classSymbol.classId] + ?.getCallableNamesForClass(classSymbol, context) + .orEmpty() + + override fun generateFunctions( + callableId: CallableId, + context: MemberGenerationContext?, + ): List { + return topLevelByClassId[callableId.classId] + ?.generateFunctions(callableId, context, firExtension = this) + .orEmpty() + } +} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirDependencyHintProvider.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirDependencyHintProvider.kt new file mode 100644 index 000000000..76ec273d0 --- /dev/null +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirDependencyHintProvider.kt @@ -0,0 +1,138 @@ +package com.squareup.anvil.compiler.k2.fir.abstraction.providers + +import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionSessionComponent +import com.squareup.anvil.compiler.k2.fir.ContributedModule +import com.squareup.anvil.compiler.k2.fir.ContributedSupertype +import com.squareup.anvil.compiler.k2.utils.fir.AnvilPredicates +import com.squareup.anvil.compiler.k2.utils.fir.requireArgumentAt +import com.squareup.anvil.compiler.k2.utils.fir.requireRegularClassSymbol +import com.squareup.anvil.compiler.k2.utils.names.ClassIds +import com.squareup.anvil.compiler.k2.utils.names.FqNames +import com.squareup.anvil.compiler.k2.utils.names.Names +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.caches.FirLazyValue +import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.declarations.evaluateAs +import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId +import org.jetbrains.kotlin.fir.declarations.unwrapVarargValue +import org.jetbrains.kotlin.fir.expressions.FirLiteralExpression +import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar +import org.jetbrains.kotlin.fir.resolve.providers.dependenciesSymbolProvider +import org.jetbrains.kotlin.fir.utils.exceptions.withFirEntry +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.utils.exceptions.checkWithAttachment +import org.jetbrains.kotlin.utils.sure + +public val FirSession.anvilFirDependencyHintProvider: AnvilFirDependencyHintProvider by FirSession.sessionComponentAccessor() + +public class AnvilFirDependencyHintProvider(session: FirSession) : + AnvilFirExtensionSessionComponent(session) { + + private val cachesFactory = session.firCachesFactory + + private fun createLazyValue(createValue: () -> V): FirLazyValue = + cachesFactory.createLazyValue(createValue) + + private val dependencyHintClassSymbols = createLazyValue { + // All hint classes are located in the `anvil.hint` package regardless of their module or type, + // and they are all top-level "classes" (really interfaces). The dependency symbol name provider + // gives us all their simple names. We can then reconstruct their ClassIds, + // and use those ClassIds to load their symbols. + // This method for finding hints is important in two ways: + // 1. Those dependency symbols will never be loaded unless they're looked up by name, + // which means that this is the only way to get what we need. + // 2. This is how the compiler handles reference linking for incremental compilation. Changes + // to the dependency hint class will trigger recompilation of the module that references it. + session.dependenciesSymbolProvider.symbolNamesProvider + .getTopLevelClassifierNamesInPackage(FqNames.anvilHintPackage) + .orEmpty() + .map { ClassId(FqNames.anvilHintPackage, it).requireRegularClassSymbol(session) } + } + + public val allDependencyContributedModules: List by dependencyHintClassSymbols.map { allHintSymbols -> + allHintSymbols.flatMap { clazzSymbol -> + + val annotation = + clazzSymbol.getAnnotationByClassId(ClassIds.anvilInternalContributedModuleHints, session) + ?: return@flatMap emptyList() + + annotation.requireArgumentAt( + name = Names.hints, + index = 1, + unwrapNamedArguments = true, + ).unwrapVarargValue() + .map { expression -> + val hint = expression.evaluateAs(session) + .sure { "can't evaluate expression: $expression" }.value as String + + val allNames = hint.split('|') + .map { ClassId.topLevel(FqName(it.trim())) } + + checkWithAttachment( + allNames.size >= 2, + { + "Expected at least two names in contributed module hint" + }, + ) { + withFirEntry("annotation", annotation) + withEntry("contributed module hint", hint) + } + + ContributedModule( + scopeType = cachesFactory.createLazyValue { allNames[0] }, + contributedType = allNames[1], + replaces = cachesFactory.createLazyValue { allNames.drop(2) }, + ) + } + } + } + + public val allDependencyContributedComponents: List + by dependencyHintClassSymbols.map { allHintSymbols -> + allHintSymbols.flatMap { clazzSymbol -> + + val annotation = + clazzSymbol.getAnnotationByClassId( + ClassIds.anvilInternalContributedComponentHints, + session, + ) + ?: return@flatMap emptyList() + + annotation.requireArgumentAt( + name = Names.hints, + index = 1, + unwrapNamedArguments = true, + ).unwrapVarargValue() + .map { expression -> + val hint = expression.evaluateAs(session) + .sure { "can't evaluate expression: $expression" }.value as String + + val allNames = hint.split('|') + .map { ClassId.topLevel(FqName(it.trim())) } + + checkWithAttachment( + allNames.size >= 2, + { + "Expected at least two names in contributed component hint" + }, + ) { + withFirEntry("annotation", annotation) + withEntry("contributed component hint", hint) + } + + ContributedSupertype( + scopeType = cachesFactory.createLazyValue { allNames[0] }, + contributedType = allNames[1], + replaces = cachesFactory.createLazyValue { allNames.drop(2) }, + ) + } + } + } + + override fun FirDeclarationPredicateRegistrar.registerPredicates() { + register(AnvilPredicates.hasAnvilInternalContributedModuleHints) + register(AnvilPredicates.hasAnvilInternalContributedComponentHints) + } +} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirProcessorProvider.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirProcessorProvider.kt new file mode 100644 index 000000000..a91bc9521 --- /dev/null +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirProcessorProvider.kt @@ -0,0 +1,53 @@ +package com.squareup.anvil.compiler.k2.fir.abstraction.providers + +import com.squareup.anvil.annotations.internal.InternalAnvilApi +import com.squareup.anvil.compiler.k2.fir.AdditionalProcessorsHolder +import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionSessionComponent +import com.squareup.anvil.compiler.k2.fir.AnvilFirProcessor +import com.squareup.anvil.compiler.k2.fir.FlushingSupertypeProcessor +import com.squareup.anvil.compiler.k2.fir.SupertypeProcessor +import com.squareup.anvil.compiler.k2.fir.TopLevelClassProcessor +import com.squareup.anvil.compiler.k2.fir.anvilContext +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.caches.getValue +import java.util.ServiceLoader + +public val FirSession.anvilFirProcessorProvider: AnvilFirProcessorProvider by FirSession.sessionComponentAccessor() + +public class AnvilFirProcessorProvider(session: FirSession) : + AnvilFirExtensionSessionComponent(session) { + + @OptIn(InternalAnvilApi::class) + private val processors by lazyValue { + + val loaded = ServiceLoader.load(AnvilFirProcessor.Factory::class.java) + + val threadLocal = AdditionalProcessorsHolder.additionalProcessors.get() + + loaded.plus(threadLocal) + .map { it.create(session.anvilContext) } + .sortedBy { it::class.qualifiedName } + .groupBy { + when (it) { + is FlushingSupertypeProcessor -> FlushingSupertypeProcessor::class + is SupertypeProcessor -> SupertypeProcessor::class + is TopLevelClassProcessor -> TopLevelClassProcessor::class + } + } + } + + public val topLevelClassProcessors: List by lazyValue { + @Suppress("UNCHECKED_CAST") + processors[TopLevelClassProcessor::class].orEmpty() as List + } + + public val supertypeProcessors: List by lazyValue { + @Suppress("UNCHECKED_CAST") + processors[SupertypeProcessor::class].orEmpty() as List + } + + public val flushingSupertypeProcessors: List by lazyValue { + @Suppress("UNCHECKED_CAST") + processors[FlushingSupertypeProcessor::class].orEmpty() as List + } +} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirSymbolProvider.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirSymbolProvider.kt new file mode 100644 index 000000000..1eec998e1 --- /dev/null +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/providers/AnvilFirSymbolProvider.kt @@ -0,0 +1,78 @@ +package com.squareup.anvil.compiler.k2.fir.abstraction.providers + +import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionSessionComponent +import com.squareup.anvil.compiler.k2.utils.fir.AnvilPredicates +import com.squareup.anvil.compiler.k2.utils.fir.hasAnnotation +import com.squareup.anvil.compiler.k2.utils.names.ClassIds +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar +import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol + +public val FirSession.anvilFirSymbolProvider: AnvilFirSymbolProvider by FirSession.sessionComponentAccessor() + +public class AnvilFirSymbolProvider( + session: FirSession, +) : AnvilFirExtensionSessionComponent(session) { + + /* + Anvil Contributes annotations + */ + internal val contributesBindingSymbols: List + by lazySymbols(AnvilPredicates.hasAnvilContributesBinding) + internal val contributesMultibindingSymbols: List + by lazySymbols(AnvilPredicates.hasAnvilContributesMultibinding) + internal val contributesSubcomponentSymbols: List + by lazySymbols(AnvilPredicates.hasAnvilContributesSubcomponent) + internal val contributesToSymbols: List + by lazySymbols(AnvilPredicates.hasAnvilContributesTo) + internal val contributesSupertypeSymbols: List + by lazySymbols(AnvilPredicates.hasAnvilContributesTo) + .map { symbols -> + symbols.filter { !it.hasAnnotation(session, ClassIds.daggerModule) } + } + internal val contributesModulesSymbols: List + by lazySymbols(AnvilPredicates.contributedModule) + internal val internalContributedModuleHintsSymbols: List + by lazySymbols(AnvilPredicates.hasAnvilInternalContributedModuleHints) + + /* + Anvil Merge annotations + */ + internal val mergeComponentSymbols: List + by lazySymbols(AnvilPredicates.hasAnvilMergeComponent) + internal val mergeSubcomponentSymbols: List + by lazySymbols(AnvilPredicates.hasAnvilMergeSubcomponent) + internal val mergeModulesSymbols: List + by lazySymbols(AnvilPredicates.hasAnvilMergeModules) + internal val mergeInterfacesSymbols: List + by lazySymbols(AnvilPredicates.hasAnvilMergeInterfaces) + + /* + Dagger annotations + */ + internal val daggerModuleSymbols: List + by lazySymbols(AnvilPredicates.hasDaggerModule) + internal val daggerBindsSymbols: List + by lazySymbols(AnvilPredicates.hasDaggerBinds) + internal val daggerComponentSymbols: List + by lazySymbols(AnvilPredicates.hasDaggerComponent) + internal val daggerSubcomponentSymbols: List + by lazySymbols(AnvilPredicates.hasDaggerSubcomponent) + internal val daggerProvidesSymbols: List + by lazySymbols(AnvilPredicates.hasDaggerProvides) + + /* + JSR-330 annotations + */ + internal val injectSymbols: List> + by lazySymbols(AnvilPredicates.hasInjectAnnotation) + + override fun FirDeclarationPredicateRegistrar.registerPredicates() { + register(AnvilPredicates.hasAnyAnvilContributes) + register(AnvilPredicates.hasAnyAnvilMerge) + register(AnvilPredicates.hasAnyDaggerAnnotation) + register(AnvilPredicates.hasInjectAnnotation) + } +} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/providers/ScopedContributionProvider.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/providers/ScopedContributionProvider.kt new file mode 100644 index 000000000..835f2b28d --- /dev/null +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/abstraction/providers/ScopedContributionProvider.kt @@ -0,0 +1,189 @@ +package com.squareup.anvil.compiler.k2.fir.abstraction.providers + +import com.squareup.anvil.annotations.ContributesBinding +import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionSessionComponent +import com.squareup.anvil.compiler.k2.fir.ContributedBinding +import com.squareup.anvil.compiler.k2.fir.ContributedModule +import com.squareup.anvil.compiler.k2.fir.ContributedSupertype +import com.squareup.anvil.compiler.k2.fir.MergedComponent +import com.squareup.anvil.compiler.k2.fir.RequiresTypesResolutionPhase +import com.squareup.anvil.compiler.k2.utils.fir.boundTypeArgumentOrNull +import com.squareup.anvil.compiler.k2.utils.fir.classListArgumentAt +import com.squareup.anvil.compiler.k2.utils.fir.contributesToAnnotations +import com.squareup.anvil.compiler.k2.utils.fir.getContributesBindingAnnotations +import com.squareup.anvil.compiler.k2.utils.fir.rankArgumentOrNull +import com.squareup.anvil.compiler.k2.utils.fir.replacesArgumentOrNull +import com.squareup.anvil.compiler.k2.utils.fir.requireAnnotationCall +import com.squareup.anvil.compiler.k2.utils.fir.requireClassId +import com.squareup.anvil.compiler.k2.utils.fir.requireScopeArgument +import com.squareup.anvil.compiler.k2.utils.fir.requireTargetClassId +import com.squareup.anvil.compiler.k2.utils.fir.resolveConeType +import com.squareup.anvil.compiler.k2.utils.names.ClassIds +import com.squareup.anvil.compiler.k2.utils.names.Names +import com.squareup.anvil.compiler.k2.utils.names.bindingModuleSibling +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension +import org.jetbrains.kotlin.fir.resolve.getSuperTypes +import org.jetbrains.kotlin.fir.resolve.providers.firProvider +import org.jetbrains.kotlin.fir.utils.exceptions.withFirSymbolEntry +import org.jetbrains.kotlin.utils.exceptions.errorWithAttachment +import kotlin.properties.Delegates + +@RequiresTypesResolutionPhase +public val FirSession.scopedContributionProvider: ScopedContributionProvider by FirSession.sessionComponentAccessor() + +public class ScopedContributionProvider(session: FirSession) : + AnvilFirExtensionSessionComponent(session) { + + @RequiresTypesResolutionPhase + private var typeResolveService: FirSupertypeGenerationExtension.TypeResolveService by Delegates.notNull() + + @RequiresTypesResolutionPhase + public val contributedModules: List by lazyValue { + session.anvilFirSymbolProvider.contributesModulesSymbols.flatMap { symbol -> + symbol.contributesToAnnotations(session).map { annotation -> + + ContributedModule( + scopeType = lazyValue { + annotation.requireScopeArgument(typeResolveService).requireClassId() + }, + contributedType = symbol.classId, + replaces = lazyValue { annotation.replacesClassIds() }, + ) + } + } + } + + @RequiresTypesResolutionPhase + public val contributedSupertypes: List by lazyValue { + session.anvilFirSymbolProvider.contributesSupertypeSymbols + .flatMap { symbol -> + symbol.contributesToAnnotations(session).map { annotation -> + + ContributedSupertype( + scopeType = lazyValue { + annotation.requireScopeArgument(typeResolveService).requireClassId() + }, + contributedType = symbol.classId, + replaces = lazyValue { annotation.replacesClassIds() }, + ) + } + } + } + + @RequiresTypesResolutionPhase + private val contributedBindingsAndBindingModules = lazyValue { + session.anvilFirSymbolProvider.contributesBindingSymbols.flatMap { symbol -> + + symbol.getContributesBindingAnnotations(session).flatMap { annotation -> + + val boundType = lazyValue { + annotation.boundTypeArgumentOrNull(session) + ?.resolveConeType(typeResolveService) + ?.requireClassId() + ?: symbol.getSuperTypes( + useSiteSession = session, + recursive = false, + lookupInterfaces = true, + ) + .singleOrNull() + ?.requireClassId() + ?: errorWithAttachment("No supertype found for @ContributesBinding type") { + withFirSymbolEntry("annotated class", symbol) + } + } + + val scopeType = lazyValue { + annotation.requireScopeArgument(typeResolveService).requireClassId() + } + val replaces = lazyValue { annotation.replacesClassIds() } + + listOf( + ContributedBinding( + scopeType = scopeType, + boundType = boundType, + contributedType = symbol.classId, + replaces = replaces, + rank = annotation.rankArgumentOrNull(session) ?: ContributesBinding.RANK_NORMAL, + ignoreQualifier = false, + isMultibinding = false, + bindingModule = symbol.classId.bindingModuleSibling, + qualifier = null, + ), + ContributedModule( + scopeType = scopeType, + contributedType = symbol.classId.bindingModuleSibling, + replaces = replaces, + ), + ) + } + } + } + + @RequiresTypesResolutionPhase + public val contributedBindings: List by contributedBindingsAndBindingModules.map { + it.filterIsInstance() + } + + @RequiresTypesResolutionPhase + public val contributedBindingModules: List by contributedBindingsAndBindingModules.map { + it.filterIsInstance() + } + + @RequiresTypesResolutionPhase + public val mergedComponents: List by lazyValue { + session.anvilFirSymbolProvider.mergeComponentSymbols.map { symbol -> + + val mergeAnnotation = symbol.requireAnnotationCall( + classId = ClassIds.anvilMergeComponent, + session = session, + resolveArguments = true, + ) + + MergedComponent( + scopeType = lazyValue { + mergeAnnotation.requireScopeArgument(typeResolveService).requireClassId() + }, + targetType = symbol.classId, + modules = lazyValue { + mergeAnnotation.classListArgumentAt(Names.modules, 1) + ?.map { it.requireTargetClassId(typeResolveService) } + .orEmpty() + }, + dependencies = lazyValue { + mergeAnnotation.classListArgumentAt(Names.dependencies, 2) + ?.map { it.requireTargetClassId(typeResolveService) } + .orEmpty() + }, + exclude = lazyValue { + mergeAnnotation.classListArgumentAt(Names.exclude, 3) + ?.map { it.requireTargetClassId(typeResolveService) } + .orEmpty() + }, + containingDeclaration = lazyValue { + session.firProvider.getFirClassifierByFqName(symbol.classId)!! + }, + mergeAnnotationCall = lazyValue { mergeAnnotation }, + ) + } + } + + private var typeResolverSet = false + internal fun isInitialized() = typeResolverSet + + @OptIn(RequiresTypesResolutionPhase::class) + internal fun bindTypeResolveService( + typeResolveService: FirSupertypeGenerationExtension.TypeResolveService, + ) { + if (!typeResolverSet) { + typeResolverSet = true + this.typeResolveService = typeResolveService + } + } + + private fun FirAnnotationCall.replacesClassIds() = replacesArgumentOrNull(session) + ?.map { it.requireTargetClassId() } + .orEmpty() +} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/BindingModuleData.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/BindingModuleData.kt deleted file mode 100644 index 96b112bb8..000000000 --- a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/BindingModuleData.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.squareup.anvil.compiler.k2.fir.contributions - -import com.squareup.anvil.compiler.k2.utils.fir.createFirAnnotation -import com.squareup.anvil.compiler.k2.utils.names.ClassIds -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.declarations.getKClassArgument -import org.jetbrains.kotlin.fir.expressions.FirAnnotation -import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall -import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression -import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotation -import org.jetbrains.kotlin.fir.expressions.impl.FirAnnotationArgumentMappingImpl -import org.jetbrains.kotlin.fir.extensions.ExperimentalTopLevelDeclarationsGenerationApi -import org.jetbrains.kotlin.fir.extensions.FirExtension -import org.jetbrains.kotlin.fir.plugin.createTopLevelClass -import org.jetbrains.kotlin.fir.resolve.fqName -import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol -import org.jetbrains.kotlin.fir.types.ConeKotlinType -import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef -import org.jetbrains.kotlin.fir.types.classId -import org.jetbrains.kotlin.fir.types.constructClassLikeType -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.Name - -@OptIn(ExperimentalTopLevelDeclarationsGenerationApi::class) -public class BindingModuleData( - public val generatedClassId: ClassId, - public val matchedClassSymbol: FirClassSymbol<*>, - firExtension: FirExtension, - session: FirSession, -) { - public val generatedClassSymbol: FirClassLikeSymbol<*> by lazy { - firExtension.createTopLevelClass( - classId = generatedClassId, - key = GeneratedBindingDeclarationKey, - classKind = ClassKind.INTERFACE, - ).apply { - replaceAnnotations( - listOf( - buildContributesToAnnotation(), - createFirAnnotation(ClassIds.daggerModule), - ), - ) - }.symbol - } - - public val contributesBindingAnnotation: FirAnnotation by lazy { - matchedClassSymbol.annotations.single { - it.fqName(session) == ClassIds.anvilContributesBinding.asSingleFqName() - } - } - - public val boundType: ConeKotlinType by lazy { - contributesBindingAnnotation.getKClassArgument(Name.identifier("boundType"), session)!! - } - - public val callableName: Name by lazy { - "bind${boundType.classId!!.shortClassName.asString()}" - .let(Name::identifier) - } - - private fun buildContributesToAnnotation(): FirAnnotation = buildAnnotation { - annotationTypeRef = buildResolvedTypeRef { - coneType = ClassIds.anvilContributesTo.constructClassLikeType() - } - // Argument mapping may be empty if merging is also happening - val newArgMapping = if (contributesBindingAnnotation.argumentMapping.mapping.isEmpty()) { - val scopeArg = (contributesBindingAnnotation as FirAnnotationCall).argumentList - .arguments - .single { (it as FirNamedArgumentExpression).name.toString() == "scope" } - - mapOf(Name.identifier("scope") to scopeArg) - } else { - contributesBindingAnnotation.argumentMapping.mapping - .filter { (key, _) -> - key.asString() == "scope" - } - } - - argumentMapping = FirAnnotationArgumentMappingImpl(null, newArgMapping) - } -} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingFirExtension.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingFirExtension.kt deleted file mode 100644 index c4c678600..000000000 --- a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingFirExtension.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.squareup.anvil.compiler.k2.fir.contributions - -import com.squareup.anvil.annotations.ContributesBinding -import com.squareup.anvil.compiler.k2.fir.AnvilFirContext -import com.squareup.anvil.compiler.k2.fir.AnvilFirDeclarationGenerationExtension -import com.squareup.anvil.compiler.k2.utils.fir.AnvilPredicates -import com.squareup.anvil.compiler.k2.utils.fir.createFirAnnotation -import com.squareup.anvil.compiler.k2.utils.names.ClassIds -import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.caches.contains -import org.jetbrains.kotlin.fir.extensions.ExperimentalTopLevelDeclarationsGenerationApi -import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar -import org.jetbrains.kotlin.fir.extensions.MemberGenerationContext -import org.jetbrains.kotlin.fir.plugin.createMemberFunction -import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol -import org.jetbrains.kotlin.fir.types.constructType -import org.jetbrains.kotlin.name.CallableId -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.Name - -/** - * Generates binding modules for every [ContributesBinding]-annotated class. - * - * Given a Kotlin source file like: - * ``` - * @ContributesBinding(Any::class) - * class Foo @Inject constructor() : Bar - * ``` - * - * We will generate the FIR equivalent of: - * ``` - * @ContributesTo(Any::class) - * @Module - * abstract class Foo_BindingModule { - * @Binds - * abstract fun bindBar(impl: Foo): Bar - * } - * ``` - */ -@OptIn(ExperimentalTopLevelDeclarationsGenerationApi::class) -public class ContributesBindingFirExtension( - anvilFirContext: AnvilFirContext, - session: FirSession, -) : AnvilFirDeclarationGenerationExtension(anvilFirContext, session) { - - override fun FirDeclarationPredicateRegistrar.registerPredicates() { - register(AnvilPredicates.hasAnvilContributesBinding) - } - - override fun getTopLevelClassIds(): Set { - return session.contributesBindingSessionComponent.generatedIdsToMatchedSymbols.keys - } - - override fun generateTopLevelClassLikeDeclaration(classId: ClassId): FirClassLikeSymbol<*> { - return session.contributesBindingSessionComponent - .bindingModuleCache - .getValue(classId, session) - .generatedClassSymbol - } - - override fun getCallableNamesForClass( - classSymbol: FirClassSymbol<*>, - context: MemberGenerationContext, - ): Set { - val cache = session.contributesBindingSessionComponent.bindingModuleCache - if (!cache.contains(classSymbol.classId)) { - return emptySet() - } - val bindingData = cache.getValue(classSymbol.classId, session) - - return setOf(bindingData.callableName) - } - - override fun generateFunctions( - callableId: CallableId, - context: MemberGenerationContext?, - ): List { - val bindingData = session.contributesBindingSessionComponent.bindingModuleCache.getValue( - context!!.owner.classId, - session, - ) - - return listOf( - createMemberFunction( - owner = context.owner, - key = GeneratedBindingDeclarationKey, - name = callableId.callableName, - returnType = bindingData.boundType, - ) { - modality = Modality.ABSTRACT - valueParameter( - name = Name.identifier("concreteType"), - type = bindingData.matchedClassSymbol.constructType(), - ) - }.apply { - replaceAnnotations(listOf(createFirAnnotation(ClassIds.daggerBinds))) - }.symbol, - ) - } -} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingFirExtensionFactory.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingFirExtensionFactory.kt deleted file mode 100644 index fe605cea3..000000000 --- a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingFirExtensionFactory.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.squareup.anvil.compiler.k2.fir.contributions - -import com.google.auto.service.AutoService -import com.squareup.anvil.compiler.k2.fir.AnvilFirContext -import com.squareup.anvil.compiler.k2.fir.AnvilFirDeclarationGenerationExtension -import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionFactory -import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension - -@AutoService(AnvilFirExtensionFactory::class) -internal class ContributesBindingFirExtensionFactory : AnvilFirDeclarationGenerationExtension.Factory { - - override fun create(anvilFirContext: AnvilFirContext): FirDeclarationGenerationExtension.Factory { - return FirDeclarationGenerationExtension.Factory { - ContributesBindingFirExtension(anvilFirContext = anvilFirContext, session = it) - } - } -} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingSessionComponent.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingSessionComponent.kt deleted file mode 100644 index 44ac1b7c4..000000000 --- a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingSessionComponent.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.squareup.anvil.compiler.k2.fir.contributions - -import com.squareup.anvil.compiler.k2.fir.AnvilFirContext -import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionSessionComponent -import com.squareup.anvil.compiler.k2.utils.fir.AnvilPredicates.hasAnvilContributesBinding -import com.squareup.anvil.compiler.k2.utils.names.joinSimpleNames -import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.caches.FirCache -import org.jetbrains.kotlin.fir.caches.firCachesFactory -import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar -import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider -import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol -import org.jetbrains.kotlin.name.ClassId - -/** - * Responsible for tracking the classes annotated with @ContributesBinding and creating + caching - * their generated Dagger module metadata. - */ -public class ContributesBindingSessionComponent( - anvilFirContext: AnvilFirContext, - session: FirSession, -) : AnvilFirExtensionSessionComponent(anvilFirContext, session) { - /** - * A map to help us track the original annotated classes' bindings, and their - * generated module IDs. - * E.g. Key: "Foo_BindingModule", Value: ClassSymbol - */ - public val generatedIdsToMatchedSymbols: Map> by lazy { - session.predicateBasedProvider.getSymbolsByPredicate(hasAnvilContributesBinding) - .filterIsInstance>() - .associateBy { - it.classId.joinSimpleNames(suffix = "BindingModule") - } - } - - public val bindingModuleCache: FirCache = - session.firCachesFactory - .createCache { key: ClassId, context -> - BindingModuleData( - key, - generatedIdsToMatchedSymbols[key] as FirClassSymbol<*>, - this@ContributesBindingSessionComponent, - session, - ) - } - - override fun FirDeclarationPredicateRegistrar.registerPredicates() { - register(hasAnvilContributesBinding) - } -} - -public val FirSession.contributesBindingSessionComponent: ContributesBindingSessionComponent - by FirSession.sessionComponentAccessor() diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingSessionComponentFactory.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingSessionComponentFactory.kt deleted file mode 100644 index dc2a135fb..000000000 --- a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingSessionComponentFactory.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.squareup.anvil.compiler.k2.fir.contributions - -import com.google.auto.service.AutoService -import com.squareup.anvil.compiler.k2.fir.AnvilFirContext -import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionFactory -import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionSessionComponent -import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent - -@AutoService(AnvilFirExtensionFactory::class) -internal class ContributesBindingSessionComponentFactory : AnvilFirExtensionSessionComponent.Factory { - - override fun create(anvilFirContext: AnvilFirContext): FirExtensionSessionComponent.Factory { - return FirExtensionSessionComponent.Factory { - ContributesBindingSessionComponent(anvilFirContext = anvilFirContext, session = it) - } - } -} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/GeneratedBindingDeclarationKey.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/GeneratedBindingDeclarationKey.kt index 2b90a99c4..77335c75c 100644 --- a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/GeneratedBindingDeclarationKey.kt +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/GeneratedBindingDeclarationKey.kt @@ -3,3 +3,4 @@ package com.squareup.anvil.compiler.k2.fir.contributions import org.jetbrains.kotlin.GeneratedDeclarationKey internal object GeneratedBindingDeclarationKey : GeneratedDeclarationKey() +internal object GeneratedBindingHintKey : GeneratedDeclarationKey() diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/internal/AnvilLogger.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/internal/AnvilLogger.kt new file mode 100644 index 000000000..b4951f432 --- /dev/null +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/internal/AnvilLogger.kt @@ -0,0 +1,76 @@ +package com.squareup.anvil.compiler.k2.fir.internal + +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.INFO +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_WARNING +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.WARNING +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.messages.MessageRenderer.PLAIN_FULL_PATHS +import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector +import java.io.PrintWriter +import java.io.StringWriter + +public interface AnvilLogger { + public val isVerbose: Boolean + + public val infoWriter: PrintWriter + public val warnWriter: PrintWriter + public val errorWriter: PrintWriter + + public fun info(message: String) + public fun warn(message: String) + public fun error(message: String) + + public fun exception(e: Throwable) +} + +public inline fun AnvilLogger.info(message: () -> String) { + if (isVerbose) { + info(message()) + } +} + +public class MessageCollectorBackedAnvilLogger( + override val isVerbose: Boolean, + isInfoAsWarnings: Boolean, + public val messageCollector: MessageCollector = defaultMessageCollector(isVerbose), +) : AnvilLogger { + + private companion object { + const val PREFIX = "[anvil] " + fun defaultMessageCollector(isVerbose: Boolean) = + PrintingMessageCollector(System.err, PLAIN_FULL_PATHS, isVerbose) + } + + override val errorWriter: PrintWriter = makeWriter(ERROR) + override val warnWriter: PrintWriter = makeWriter(STRONG_WARNING) + override val infoWriter: PrintWriter = makeWriter(if (isInfoAsWarnings) WARNING else INFO) + + override fun info(message: String) { + if (isVerbose) { + messageCollector.report(INFO, PREFIX + message) + } + } + + override fun warn(message: String) { + messageCollector.report(WARNING, PREFIX + message) + } + + override fun error(message: String) { + messageCollector.report(ERROR, PREFIX + message) + } + + override fun exception(e: Throwable) { + val stacktrace = run { + val writer = StringWriter() + e.printStackTrace(PrintWriter(writer)) + writer.toString() + } + messageCollector.report(ERROR, PREFIX + "An exception occurred: " + stacktrace) + } + + private fun makeWriter(severity: CompilerMessageSeverity): PrintWriter { + return PrintWriter(MessageCollectorBackedWriter(messageCollector, severity)) + } +} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/internal/AnvilMessageCollector.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/internal/AnvilMessageCollector.kt new file mode 100644 index 000000000..76c27f083 --- /dev/null +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/internal/AnvilMessageCollector.kt @@ -0,0 +1,60 @@ +package com.squareup.anvil.compiler.k2.fir.internal + +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation +import org.jetbrains.kotlin.cli.common.messages.GroupingMessageCollector +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.common.messages.MessageRenderer +import java.io.Writer + +internal class AnvilMessageCollector( + private val messageRenderer: MessageRenderer, +) : MessageCollector { + override fun clear() { + } + + override fun hasErrors(): Boolean = false + + override fun report( + severity: CompilerMessageSeverity, + message: String, + location: CompilerMessageSourceLocation?, + ) { + + val rendered = messageRenderer.render(severity, message, location) + + println(rendered) + + // when (severity) { + // CompilerMessageSeverity.EXCEPTION -> TODO() + // CompilerMessageSeverity.ERROR -> TODO() + // CompilerMessageSeverity.STRONG_WARNING -> TODO() + // CompilerMessageSeverity.WARNING -> TODO() + // CompilerMessageSeverity.INFO -> TODO() + // CompilerMessageSeverity.LOGGING -> TODO() + // CompilerMessageSeverity.OUTPUT -> TODO() + // } + } +} + +public class MessageCollectorBackedWriter( + private val messageCollector: MessageCollector, + private val severity: CompilerMessageSeverity, +) : Writer() { + override fun write(buffer: CharArray, offset: Int, length: Int) { + val message = String(buffer, offset, length).trim().trim('\n', '\r') + if (message.isNotEmpty()) { + messageCollector.report(severity, message) + } + } + + override fun flush() { + if (messageCollector is GroupingMessageCollector) { + messageCollector.flush() + } + } + + override fun close() { + flush() + } +} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/merging/AnnotationMergingGenerator.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/merging/AnnotationMergingGenerator.kt new file mode 100644 index 000000000..f746b2d56 --- /dev/null +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/merging/AnnotationMergingGenerator.kt @@ -0,0 +1,185 @@ +package com.squareup.anvil.compiler.k2.fir.merging + +import com.google.auto.service.AutoService +import com.squareup.anvil.compiler.k2.fir.AbstractAnvilFirProcessorFactory +import com.squareup.anvil.compiler.k2.fir.AnvilFirContext +import com.squareup.anvil.compiler.k2.fir.AnvilFirProcessor +import com.squareup.anvil.compiler.k2.fir.FlushingSupertypeProcessor +import com.squareup.anvil.compiler.k2.fir.RequiresTypesResolutionPhase +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.anvilFirDependencyHintProvider +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.anvilFirSymbolProvider +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.scopedContributionProvider +import com.squareup.anvil.compiler.k2.utils.fir.createClassListArgument +import com.squareup.anvil.compiler.k2.utils.fir.createFirAnnotationCall +import com.squareup.anvil.compiler.k2.utils.names.ClassIds +import com.squareup.anvil.compiler.k2.utils.names.Names +import com.squareup.anvil.compiler.k2.utils.psi.ktPsiFactory +import com.squareup.anvil.compiler.k2.utils.stdlib.mapToSet +import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.fakeElement +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration +import org.jetbrains.kotlin.fir.declarations.utils.classId +import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall +import org.jetbrains.kotlin.fir.expressions.builder.buildArgumentList +import org.jetbrains.kotlin.fir.psi +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression +import org.jetbrains.kotlin.toKtPsiSourceElement + +@AutoService(AnvilFirProcessor.Factory::class) +public class AnnotationMergingGeneratorFactory : + AbstractAnvilFirProcessorFactory(::AnnotationMergingGenerator) + +/** + * This generator merges all contributed Dagger modules on the classpath and includes them on the + * component annotated with `@MergeComponent`. + */ +public class AnnotationMergingGenerator( + override val anvilContext: AnvilFirContext, +) : FlushingSupertypeProcessor() { + + private val mergedComponentIds by lazyValue { + session.anvilFirSymbolProvider.mergeComponentSymbols.mapToSet { it.classId } + } + + @RequiresTypesResolutionPhase + private val mergedModulesByScope by lazyValue { + val sourceModules = session.scopedContributionProvider.contributedModules + val generatedModules = session.scopedContributionProvider.contributedBindingModules + val dependencyModules = session.anvilFirDependencyHintProvider.allDependencyContributedModules + + (sourceModules + generatedModules + dependencyModules).groupBy { it.scopeType.getValue() } + } + + override fun shouldProcess(declaration: FirClassLikeDeclaration): Boolean = + declaration.classId in mergedComponentIds + + @RequiresTypesResolutionPhase + public override fun generateAnnotation( + classLikeDeclaration: FirClassLikeDeclaration, + ): FirAnnotationCall { + val mergedComponent = + session.scopedContributionProvider.mergedComponents + .single { it.containingDeclaration.getValue().classId == classLikeDeclaration.classId } + + val mergeScopeId = mergedComponent.scopeType.getValue() + + val containingDeclaration = mergedComponent.containingDeclaration.getValue() + + val annotationModules = mergedComponent.modules.getValue() + + val mergedModules = mergedModulesByScope[mergeScopeId].orEmpty() + .map { it.contributedType } + .plus(annotationModules) + .sortedBy { it.asString() } + + val mergeAnnotation = mergedComponent.mergeAnnotationCall.getValue() + + val newAnnotationCallPsi = mergeAnnotation.psi?.let { psiEntry -> + buildNewAnnotationPsi( + oldAnnotationCall = psiEntry as KtAnnotationEntry, + mergedModules = mergedModules, + ) + } + + val newSource = + newAnnotationCallPsi?.toKtPsiSourceElement(KtFakeSourceElementKind.PluginGenerated) + ?: mergeAnnotation.source?.fakeElement(KtFakeSourceElementKind.PluginGenerated) + + return createFirAnnotationCall( + type = ClassIds.daggerComponent, + containingDeclarationSymbol = containingDeclaration.symbol, + argumentList = buildArgumentList { + + source = mergeAnnotation.source?.fakeElement(KtFakeSourceElementKind.PluginGenerated) + + if (mergedModules.isNotEmpty()) { + arguments += createClassListArgument(Names.modules, mergedModules, session) + } + + val mergeDeps = mergedComponent.dependencies.getValue() + if (mergeDeps.isEmpty()) { + arguments += createClassListArgument(Names.dependencies, mergeDeps, session) + } + }, + source = newSource, + ktPsiFactory = containingDeclaration.psi?.ktPsiFactory(), + ) + } + + private fun buildNewAnnotationPsi( + oldAnnotationCall: KtAnnotationEntry, + mergedModules: List, + ): KtAnnotationEntry { + + val oldAnnotationArguments = oldAnnotationCall.valueArgumentList + ?.arguments + .orEmpty() + + // `modules = [SomeModule::class]` + val oldModulesArg = oldAnnotationArguments + // `Component` is a Java annotation with default argument values, + // so its arguments can be missing or in any order, but they must be named if they're present. + .firstOrNull { arg -> + val name = arg.getArgumentName() + name == null || name.text == "modules" + } + + // `SomeModule::class, SomeOtherModule::class` + val existingModuleArgExpressions = + (oldModulesArg?.getArgumentExpression() as? KtCollectionLiteralExpression) + ?.innerExpressions + ?.map { it.text } + .orEmpty() + + val imports = oldAnnotationCall.containingKtFile.importDirectives + .associate { imp -> + val fqName = imp.importedReference?.text + ?: error("import directive doesn't have a reference? $imp") + + val name = imp.aliasName + ?: imp.importedReference?.text?.substringAfterLast('.') + ?: error("import directive doesn't have a reference or alias? ${imp.text}") + + fqName to name + } + + val newModulesMaybeImported = mergedModules.map { it.asFqNameString() } + .map { moduleFqName -> + imports[moduleFqName] ?: moduleFqName + } + + val allClassArgs = existingModuleArgExpressions + .plus(newModulesMaybeImported.map { "$it::class" }) + .distinct() + + val factory = oldAnnotationCall.ktPsiFactory() + + val classArgList = allClassArgs.joinToString(separator = ", ") + + val newModulesText = "modules = [$classArgList]" + + val componentCall = ClassIds.daggerComponent.asFqNameString().let { fqString -> + imports[fqString] ?: fqString + } + + val newAnnotationText = when { + oldAnnotationArguments.isEmpty() -> "@$componentCall($newModulesText)" + oldModulesArg != null -> oldAnnotationCall.text.replace( + oldValue = oldModulesArg.text, + newValue = newModulesText, + ) + else -> oldAnnotationArguments.map { it.text } + .plus(newModulesText) + .joinToString( + separator = ",\n", + prefix = "@$componentCall(\n", + postfix = "\n)", + ) { " $it" } + } + + return factory.createAnnotationEntry(newAnnotationText) + } +} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/merging/AnvilFirAnnotationMergingExtension.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/merging/AnvilFirAnnotationMergingExtension.kt deleted file mode 100644 index 0b997d446..000000000 --- a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/merging/AnvilFirAnnotationMergingExtension.kt +++ /dev/null @@ -1,271 +0,0 @@ -package com.squareup.anvil.compiler.k2.fir.merging - -import com.google.auto.service.AutoService -import com.squareup.anvil.compiler.k2.fir.AnvilFirContext -import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionFactory -import com.squareup.anvil.compiler.k2.fir.AnvilFirSupertypeGenerationExtension -import com.squareup.anvil.compiler.k2.fir.contributions.BindingModuleData -import com.squareup.anvil.compiler.k2.fir.contributions.contributesBindingSessionComponent -import com.squareup.anvil.compiler.k2.utils.fir.AnvilPredicates -import com.squareup.anvil.compiler.k2.utils.fir.argumentAt -import com.squareup.anvil.compiler.k2.utils.fir.classListArgumentAt -import com.squareup.anvil.compiler.k2.utils.fir.contributesToScope -import com.squareup.anvil.compiler.k2.utils.fir.fqName -import com.squareup.anvil.compiler.k2.utils.fir.requireFqName -import com.squareup.anvil.compiler.k2.utils.fir.requireScopeArgument -import com.squareup.anvil.compiler.k2.utils.fir.resolveConeType -import com.squareup.anvil.compiler.k2.utils.fir.setAnnotationType -import com.squareup.anvil.compiler.k2.utils.fir.toGetClassCall -import com.squareup.anvil.compiler.k2.utils.names.ClassIds -import com.squareup.anvil.compiler.k2.utils.names.Names -import com.squareup.anvil.compiler.k2.utils.psi.ktPsiFactory -import org.jetbrains.kotlin.KtFakeSourceElementKind -import org.jetbrains.kotlin.fakeElement -import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration -import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall -import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCallCopy -import org.jetbrains.kotlin.fir.expressions.builder.buildArgumentList -import org.jetbrains.kotlin.fir.expressions.builder.buildArrayLiteral -import org.jetbrains.kotlin.fir.expressions.builder.buildNamedArgumentExpression -import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar -import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension -import org.jetbrains.kotlin.fir.extensions.predicate.DeclarationPredicate -import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider -import org.jetbrains.kotlin.fir.psi -import org.jetbrains.kotlin.fir.resolve.fqName -import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.fir.types.ConeKotlinType -import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtAnnotationEntry -import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression -import org.jetbrains.kotlin.toKtPsiSourceElement - -/** - * This extension merges all contributed Dagger modules on the classpath and includes them on the - * component annotated with `@MergeComponent`. - */ -public class AnvilFirAnnotationMergingExtension( - anvilFirContext: AnvilFirContext, - session: FirSession, -) : AnvilFirSupertypeGenerationExtension(anvilFirContext, session) { - - @AutoService(AnvilFirExtensionFactory::class) - public class Factory : AnvilFirSupertypeGenerationExtension.Factory { - override fun create(anvilFirContext: AnvilFirContext): FirSupertypeGenerationExtension.Factory { - return FirSupertypeGenerationExtension.Factory { session -> - AnvilFirAnnotationMergingExtension(anvilFirContext, session) - } - } - } - - private companion object { - private val PREDICATE = DeclarationPredicate.create { - annotated(ClassIds.anvilMergeComponent.asSingleFqName()) - } - } - - override fun FirDeclarationPredicateRegistrar.registerPredicates() { - register(AnvilPredicates.hasMergeComponentAnnotation) - register(AnvilPredicates.contributedModule) - } - - override fun computeAdditionalSupertypes( - classLikeDeclaration: FirClassLikeDeclaration, - resolvedSupertypes: List, - typeResolver: TypeResolveService, - ): List { - - val componentAnnotation = classLikeDeclaration.annotations - .single { it.fqName(session) == ClassIds.anvilMergeComponent.asSingleFqName() } as FirAnnotationCall - - val mergeScope = componentAnnotation.requireScopeArgument().resolveConeType(typeResolver) - val mergeScopeName = mergeScope.requireFqName() - - val oldModules = componentAnnotation - .classListArgumentAt(Names.modules, index = 1) - .orEmpty() - - classLikeDeclaration.replaceAnnotations( - classLikeDeclaration.annotations + buildAnnotationCallCopy(componentAnnotation) { - setAnnotationType( - newType = ClassIds.daggerComponent, - ktPsiFactoryOrNull = classLikeDeclaration.psi?.ktPsiFactory(), - ) - - val contributedModules = getContributedModules(mergeScopeName, typeResolver) - val generatedBindingModules = getGeneratedBindingModules(mergeScope, typeResolver) - - val mergedModules = contributedModules.map { it.fqName() } + - generatedBindingModules.map { it.generatedClassId.asSingleFqName() } - - val newAnnotationCallPsi = componentAnnotation.psi?.let { - buildNewAnnotationPsi( - oldAnnotationCall = it as KtAnnotationEntry, - mergedModules = mergedModules, - ) - } - - val newSource = - newAnnotationCallPsi?.toKtPsiSourceElement(KtFakeSourceElementKind.PluginGenerated) - ?: componentAnnotation.source?.fakeElement(KtFakeSourceElementKind.PluginGenerated) - source = newSource - - argumentList = buildArgumentList { - source = componentAnnotation.source?.fakeElement(KtFakeSourceElementKind.PluginGenerated) - - arguments += buildNamedArgumentExpression { - name = Names.modules - isSpread = false - expression = buildArrayLiteral { - argumentList = buildArgumentList { - arguments += oldModules - arguments += contributedModules.map { it.toGetClassCall() } - arguments += generatedBindingModules.map { it.generatedClassSymbol.toGetClassCall() } - } - } - } - - componentAnnotation.argumentAt( - Names.dependencies, - index = 2, - unwrapNamedArguments = false, - )?.let { - arguments += it - } - } - }, - ) - - return listOf() - } - - /** - * Gets all Dagger modules annotated with @ContributesTo that match the scope being merged - */ - private fun getContributedModules( - mergeScopeName: FqName, - typeResolver: TypeResolveService, - ): List> { - return session.predicateBasedProvider - .getSymbolsByPredicate(AnvilPredicates.contributedModule) - .mapNotNull { moduleClass -> - if (moduleClass !is FirClassLikeSymbol<*>) return@mapNotNull null - - if (moduleClass.contributesToScope( - scope = mergeScopeName, - session = session, - typeResolveService = typeResolver, - ) - ) { - moduleClass - } else { - null - } - } - } - - /** - * Gets data for all Dagger modules generated from a @ContributesBinding-annotated class matching - * the scope that is being merged - */ - private fun getGeneratedBindingModules( - mergeScope: ConeKotlinType, - typeResolver: TypeResolveService, - ): Set { - return session.contributesBindingSessionComponent - .generatedIdsToMatchedSymbols - .keys - .mapNotNull { id: ClassId -> - val bindingData = - session.contributesBindingSessionComponent.bindingModuleCache.getValue(id, session) - val bindingScope = - (bindingData.contributesBindingAnnotation as FirAnnotationCall).requireScopeArgument() - .resolveConeType(typeResolver) - - if (bindingScope == mergeScope) { - bindingData - } else { - null - } - } - .toSet() - } - - private fun buildNewAnnotationPsi( - oldAnnotationCall: KtAnnotationEntry, - mergedModules: List, - ): KtAnnotationEntry { - - val oldAnnotationArguments = oldAnnotationCall.valueArgumentList - ?.arguments - .orEmpty() - - // `modules = [SomeModule::class]` - val oldModulesArg = oldAnnotationArguments - // `Component` is a Java annotation with default argument values, - // so its arguments can be missing or in any order, but they must be named if they're present. - .firstOrNull { arg -> - val name = arg.getArgumentName() - name == null || name.text == "modules" - } - - // `SomeModule::class, SomeOtherModule::class` - val existingModuleArgExpressions = - (oldModulesArg?.getArgumentExpression() as? KtCollectionLiteralExpression) - ?.innerExpressions - ?.map { it.text } - .orEmpty() - - val imports = oldAnnotationCall.containingKtFile.importDirectives - .associate { imp -> - val fqName = imp.importedReference?.text - ?: error("import directive doesn't have a reference? $imp") - - val name = imp.aliasName - ?: imp.importedReference?.text?.substringAfterLast('.') - ?: error("import directive doesn't have a reference or alias? ${imp.text}") - - fqName to name - } - - val newModulesMaybeImported = mergedModules.map { it.asString() } - .map { moduleFqName -> - imports[moduleFqName] ?: moduleFqName - } - - val allClassArgs = existingModuleArgExpressions - .plus(newModulesMaybeImported.map { "$it::class" }) - .distinct() - - val factory = oldAnnotationCall.ktPsiFactory() - - val classArgList = allClassArgs.joinToString(separator = ", ") - - val newModulesText = "modules = [$classArgList]" - - val componentCall = ClassIds.daggerComponent.asString().let { fqString -> - imports[fqString] ?: fqString - } - - val newAnnotationText = when { - oldAnnotationArguments.isEmpty() -> "@$componentCall($newModulesText)" - oldModulesArg != null -> oldAnnotationCall.text.replace(oldModulesArg.text, newModulesText) - else -> oldAnnotationArguments.map { it.text } - .plus(newModulesText) - .joinToString( - separator = ",\n", - prefix = "@$componentCall(\n", - postfix = "\n)", - ) { " $it" } - } - - return factory.createAnnotationEntry(newAnnotationText) - } - - override fun needTransformSupertypes(declaration: FirClassLikeDeclaration): Boolean { - return session.predicateBasedProvider.matches(PREDICATE, declaration) - } -} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/merging/AnvilFirInterfaceMergingExtension.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/merging/AnvilFirInterfaceMergingExtension.kt deleted file mode 100644 index e4eca2a73..000000000 --- a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/merging/AnvilFirInterfaceMergingExtension.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.squareup.anvil.compiler.k2.fir.merging - -import com.google.auto.service.AutoService -import com.squareup.anvil.compiler.k2.fir.AnvilFirContext -import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionFactory -import com.squareup.anvil.compiler.k2.fir.AnvilFirSupertypeGenerationExtension -import com.squareup.anvil.compiler.k2.utils.fir.AnvilPredicates -import com.squareup.anvil.compiler.k2.utils.fir.contributesToScope -import com.squareup.anvil.compiler.k2.utils.fir.hasAnnotation -import com.squareup.anvil.compiler.k2.utils.fir.requireScopeArgument -import com.squareup.anvil.compiler.k2.utils.fir.resolveConeType -import com.squareup.anvil.compiler.k2.utils.names.ClassIds -import com.squareup.anvil.compiler.k2.utils.stdlib.mapToSet -import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration -import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall -import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar -import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension -import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider -import org.jetbrains.kotlin.fir.plugin.createConeType -import org.jetbrains.kotlin.fir.resolve.fqName -import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.fir.types.ConeKotlinType -import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef -import org.jetbrains.kotlin.fir.types.classId - -/** - * This extension finds all contributed component interfaces and adds them as super types to Dagger - * components annotated with `@MergeComponent` - */ -public class AnvilFirInterfaceMergingExtension( - anvilFirContext: AnvilFirContext, - session: FirSession, -) : AnvilFirSupertypeGenerationExtension(anvilFirContext, session) { - - @AutoService(AnvilFirExtensionFactory::class) - public class Factory : AnvilFirSupertypeGenerationExtension.Factory { - override fun create(anvilFirContext: AnvilFirContext): FirSupertypeGenerationExtension.Factory { - return FirSupertypeGenerationExtension.Factory { session -> - AnvilFirInterfaceMergingExtension(anvilFirContext, session) - } - } - } - - override fun FirDeclarationPredicateRegistrar.registerPredicates() { - register(AnvilPredicates.hasAnvilContributesTo) - register(AnvilPredicates.hasModuleAnnotation) - } - - override fun computeAdditionalSupertypes( - classLikeDeclaration: FirClassLikeDeclaration, - resolvedSupertypes: List, - typeResolver: TypeResolveService, - ): List { - - val existingSupertypes = resolvedSupertypes.mapToSet { it.coneType.classId } - - return classLikeDeclaration.annotations - .filter { it.fqName(session) == ClassIds.anvilMergeComponent.asSingleFqName() } - .flatMap { annotation -> - - val scopeFqName = (annotation as FirAnnotationCall).requireScopeArgument() - .resolveConeType(typeResolver) - .classId!! - .asSingleFqName() - - session.predicateBasedProvider - .getSymbolsByPredicate(AnvilPredicates.hasAnvilContributesTo) - .filterIsInstance>() - .mapNotNull { contributed -> - - val classId = contributed.classId - - // If the class is already a supertype, we can't add it again. - if (classId in existingSupertypes) return@mapNotNull null - - // If it's a contributed module, we don't add it here - if (contributed.hasAnnotation(ClassIds.daggerModule, session)) { - return@mapNotNull null - } - - // Only merge contributions to this scope - if (!contributed.contributesToScope( - scopeFqName, - session, - typeResolver, - ) - ) { - return@mapNotNull null - } - - classId.createConeType(session) - } - } - } - - override fun needTransformSupertypes(declaration: FirClassLikeDeclaration): Boolean { - return session.predicateBasedProvider.matches( - AnvilPredicates.hasMergeComponentAnnotation, - declaration, - ) - } -} diff --git a/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/merging/InterfaceMergingGenerator.kt b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/merging/InterfaceMergingGenerator.kt new file mode 100644 index 000000000..6c28a7239 --- /dev/null +++ b/compiler-k2/src/main/kotlin/com/squareup/anvil/compiler/k2/fir/merging/InterfaceMergingGenerator.kt @@ -0,0 +1,70 @@ +package com.squareup.anvil.compiler.k2.fir.merging + +import com.google.auto.service.AutoService +import com.squareup.anvil.compiler.k2.fir.AbstractAnvilFirProcessorFactory +import com.squareup.anvil.compiler.k2.fir.AnvilFirContext +import com.squareup.anvil.compiler.k2.fir.AnvilFirProcessor +import com.squareup.anvil.compiler.k2.fir.RequiresTypesResolutionPhase +import com.squareup.anvil.compiler.k2.fir.SupertypeProcessor +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.anvilFirDependencyHintProvider +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.anvilFirSymbolProvider +import com.squareup.anvil.compiler.k2.fir.abstraction.providers.scopedContributionProvider +import com.squareup.anvil.compiler.k2.utils.stdlib.mapToSet +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration +import org.jetbrains.kotlin.fir.declarations.utils.classId +import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension +import org.jetbrains.kotlin.fir.plugin.createConeType +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef +import org.jetbrains.kotlin.fir.types.classId + +@AutoService(AnvilFirProcessor.Factory::class) +public class InterfaceMergingGeneratorFactory : + AbstractAnvilFirProcessorFactory(::InterfaceMergingGenerator) + +/** + * This extension finds all contributed component interfaces and adds them as super types to Dagger + * components annotated with `@MergeComponent` + */ +public class InterfaceMergingGenerator( + override val anvilContext: AnvilFirContext, +) : SupertypeProcessor() { + + private val mergedComponentIds by lazyValue { + session.anvilFirSymbolProvider.mergeComponentSymbols.mapToSet { it.classId } + } + + @RequiresTypesResolutionPhase + private val mergedSupertypesByScope by lazyValue { + val sourceSupers = session.scopedContributionProvider.contributedSupertypes + val dependencySupers = session.anvilFirDependencyHintProvider.allDependencyContributedComponents + + (sourceSupers + dependencySupers).groupBy { it.scopeType.getValue() } + } + + override fun shouldProcess(declaration: FirClassLikeDeclaration): Boolean { + return declaration.classId in mergedComponentIds + } + + @OptIn(RequiresTypesResolutionPhase::class) + override fun addSupertypes( + classLikeDeclaration: FirClassLikeDeclaration, + resolvedSupertypes: List, + typeResolver: FirSupertypeGenerationExtension.TypeResolveService, + ): List { + + val existingSupertypes = resolvedSupertypes.mapToSet { it.coneType.classId } + + val mergedComponent = + session.scopedContributionProvider.mergedComponents + .single { it.containingDeclaration.getValue().classId == classLikeDeclaration.classId } + + val mergeScopeId = mergedComponent.scopeType.getValue() + + return mergedSupertypesByScope[mergeScopeId] + ?.filter { it.contributedType !in existingSupertypes } + ?.map { it.contributedType.createConeType(session) } + .orEmpty() + } +} diff --git a/compiler-k2/src/test/kotlin/com/squareup/anvil/compiler/k2/fir/AnnotationMergingTest.kt b/compiler-k2/src/test/kotlin/com/squareup/anvil/compiler/k2/fir/AnnotationMergingTest.kt new file mode 100644 index 000000000..0906324ab --- /dev/null +++ b/compiler-k2/src/test/kotlin/com/squareup/anvil/compiler/k2/fir/AnnotationMergingTest.kt @@ -0,0 +1,156 @@ +package com.squareup.anvil.compiler.k2.fir + +import com.rickbusarow.kase.stdlib.div +import com.squareup.anvil.compiler.testing.CompilationModeTest +import com.squareup.anvil.compiler.testing.TestNames +import com.squareup.anvil.compiler.testing.classgraph.allMergedModulesForComponent +import com.squareup.anvil.compiler.testing.classgraph.componentInterface +import com.squareup.anvil.compiler.testing.classgraph.fqNames +import io.kotest.matchers.shouldBe +import org.jetbrains.kotlin.name.FqName +import org.junit.jupiter.api.TestFactory + +class AnnotationMergingTest : CompilationModeTest(MODE_DEFAULTS.filter { it.isK2 && !it.useKapt }) { + + @TestFactory + fun `contributed modules from dependency compilations are merged`() = testFactory { + + val dep1 = compile2( + """ + package com.squareup.test.dep1 + + @dagger.Module + @com.squareup.anvil.annotations.ContributesTo(Unit::class) + interface DependencyModule1 + """.trimIndent(), + """ + package anvil.hint.dep1 + + val myHint = Unit + + fun things() { + println("hello world") + } + + interface Dep1HintInterface + """.trimIndent(), + workingDir = workingDir / "dep1", + ) + + val dep2 = compile2( + """ + package com.squareup.test.dep2 + + interface Dep2A + + @com.squareup.anvil.annotations.ContributesBinding(Unit::class) + class Dep2AImpl @javax.inject.Inject constructor() : Dep2A + + @dagger.Module + @com.squareup.anvil.annotations.ContributesTo(Unit::class) + interface DependencyModule2 + """.trimIndent(), + workingDir = workingDir / "dep2", + ) + + compile2( + """ + package com.squareup.test + + fun foo(d2a: com.squareup.test.dep2.Dep2A) {} + + @dagger.Module + @com.squareup.anvil.annotations.ContributesTo(Unit::class) + interface LocalProjectModule + + interface LocalA + + @com.squareup.anvil.annotations.ContributesBinding(Unit::class) + class LocalAImpl @javax.inject.Inject constructor() : LocalA + + @com.squareup.anvil.annotations.MergeComponent(Unit::class) + interface ComponentInterface + """.trimIndent(), + configuration = { + it.copy( + compilationClasspath = it.compilationClasspath + .plus(dep1.jar) + .plus(dep2.jar), + ) + }, + workingDir = workingDir / "consumer", + ) { + + val hintPackage = scanResult.getPackageInfo("anvil.hint") + + // hintPackage.classInfo.names shouldBe listOf( + // "anvil.hint.AnvilModuleHints_7395f7a5", + // "anvil.hint.AnvilModuleHints_b9fd254f", + // "anvil.hint.AnvilModuleHints_c8e109e7", + // "anvil.hint.AnvilModuleHints_db380a0a", + // "anvil.hint.AnvilModuleHints_ee672a89", + // ) + + scanResult + .allMergedModulesForComponent(TestNames.componentInterface.asFqNameString()) + .fqNames() shouldBe listOf( + "com.squareup.test.LocalAImpl_BindingModule", + "com.squareup.test.LocalProjectModule", + "com.squareup.test.dep1.DependencyModule1", + "com.squareup.test.dep2.Dep2AImpl_BindingModule", + "com.squareup.test.dep2.DependencyModule2", + ).map(::FqName) + } + } + + @TestFactory + fun `contributed supertypes from dependency compilations are merged`() = testFactory { + + val dep1 = compile2( + """ + package com.squareup.test.dep1 + + @com.squareup.anvil.annotations.ContributesTo(Unit::class) + interface DependencySuper1 + """.trimIndent(), + workingDir = workingDir / "dep1", + ) + + val dep2 = compile2( + """ + package com.squareup.test.dep2 + + @com.squareup.anvil.annotations.ContributesTo(Unit::class) + interface DependencySuper2 + """.trimIndent(), + workingDir = workingDir / "dep2", + ) + + compile2( + """ + package com.squareup.test + + @com.squareup.anvil.annotations.ContributesTo(Unit::class) + interface LocalSuper + + @com.squareup.anvil.annotations.MergeComponent(Unit::class) + interface ComponentInterface + """.trimIndent(), + configuration = { + it.copy( + compilationClasspath = it.compilationClasspath + .plus(dep1.jar) + .plus(dep2.jar), + ) + }, + workingDir = workingDir / "consumer", + ) { + + scanResult.componentInterface.interfaces.names.sorted() shouldBe listOf( + "com.squareup.test.LocalSuper", + "com.squareup.test.dep1.DependencySuper1", + "com.squareup.test.dep2.DependencySuper2", + ) + } + } +} diff --git a/compiler-k2/src/test/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingFirExtensionTest.kt b/compiler-k2/src/test/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingFirExtensionTest.kt index 342d23b47..7b32794c5 100644 --- a/compiler-k2/src/test/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingFirExtensionTest.kt +++ b/compiler-k2/src/test/kotlin/com/squareup/anvil/compiler/k2/fir/contributions/ContributesBindingFirExtensionTest.kt @@ -3,8 +3,8 @@ package com.squareup.anvil.compiler.k2.fir.contributions import com.squareup.anvil.compiler.k2.utils.names.ClassIds import com.squareup.anvil.compiler.testing.CompilationModeTest import com.squareup.anvil.compiler.testing.classgraph.getAnnotationInfo +import com.squareup.anvil.compiler.testing.classgraph.hints import com.squareup.anvil.compiler.testing.classgraph.moduleNames -import com.squareup.anvil.compiler.testing.reflect.contributesToAnnotation import com.squareup.anvil.compiler.testing.reflect.contributingObject import com.squareup.anvil.compiler.testing.reflect.generatedBindingModule import com.squareup.anvil.compiler.testing.reflect.isAbstract @@ -42,10 +42,14 @@ class ContributesBindingFirExtensionTest : CompilationModeTest( object ContributingObject : ParentInterface """.trimIndent(), ) { - val contributesToScope = - classLoader.contributingObject.generatedBindingModule.contributesToAnnotation.scope - contributesToScope.java.name shouldBe "com.squareup.test.other.AppScope" + val singleClass = scanResult.getPackageInfo("anvil.hint").classInfo.single() + + val annotation = singleClass.getAnnotationInfo(ClassIds.anvilInternalContributedModuleHints) + + annotation.hints shouldBe listOf( + "com.squareup.test.other.AppScope|com.squareup.test.ContributingObject_BindingModule", + ) } } diff --git a/compiler-testing/api/compiler-testing.api b/compiler-testing/api/compiler-testing.api index 79777031b..9c63baeda 100644 --- a/compiler-testing/api/compiler-testing.api +++ b/compiler-testing/api/compiler-testing.api @@ -208,6 +208,14 @@ public final class com/squareup/anvil/compiler/testing/TestNamesKt { public static final fun sibling (Lorg/jetbrains/kotlin/name/FqName;Ljava/lang/String;)Lorg/jetbrains/kotlin/name/FqName; } +public final class com/squareup/anvil/compiler/testing/classgraph/AnnotationInfoKt { + public static final fun getHints (Lio/github/classgraph/AnnotationInfo;)Ljava/util/List; + public static final fun getModuleNames (Lio/github/classgraph/AnnotationInfo;)Ljava/util/List; + public static final fun getScope (Lio/github/classgraph/AnnotationInfo;)Ljava/lang/String; + public static final fun requireParameter (Lio/github/classgraph/AnnotationInfo;Lorg/jetbrains/kotlin/name/Name;Z)Ljava/lang/Object; + public static synthetic fun requireParameter$default (Lio/github/classgraph/AnnotationInfo;Lorg/jetbrains/kotlin/name/Name;ZILjava/lang/Object;)Ljava/lang/Object; +} + public abstract interface class com/squareup/anvil/compiler/testing/classgraph/ClassGraphAsserts : com/squareup/anvil/compiler/testing/MoreAsserts { public fun shouldBe (Lio/github/classgraph/ClassInfoList;Ljava/util/Collection;)V public fun shouldContainClass (Lio/github/classgraph/ClassInfoList;Ljava/lang/String;)Lio/github/classgraph/ClassInfo; @@ -251,7 +259,6 @@ public final class com/squareup/anvil/compiler/testing/classgraph/ClassInfoKt { public static final fun fqNames (Ljava/util/Collection;)Ljava/util/List; public static final fun getAnnotationInfo (Lio/github/classgraph/ClassInfo;Lorg/jetbrains/kotlin/name/ClassId;)Lio/github/classgraph/AnnotationInfo; public static final fun getClassId (Lio/github/classgraph/ClassInfo;)Lorg/jetbrains/kotlin/name/ClassId; - public static final fun getModuleNames (Lio/github/classgraph/AnnotationInfo;)Ljava/util/List; } public final class com/squareup/anvil/compiler/testing/classgraph/MethodInfoKt { @@ -380,6 +387,7 @@ public final class com/squareup/anvil/compiler/testing/compilation/Compile2Resul public final class com/squareup/anvil/compiler/testing/compilation/Compile2ResultKt { public static final fun classGraphResult (Ljava/util/List;)Lio/github/classgraph/ScanResult; public static final fun compilationErrorLine (Lcom/squareup/anvil/compiler/testing/compilation/Compile2Result;)Ljava/lang/String; + public static final fun isJarFile (Ljava/io/File;)Z public static final fun requireIsJarFile (Ljava/io/File;)Ljava/io/File; } @@ -454,9 +462,11 @@ public final class com/squareup/anvil/compiler/testing/reflect/ClassKt { public static final fun factoryClass (Ljava/lang/Class;)Ljava/lang/Class; public static final fun field (Ljava/lang/Class;)Lkotlin/properties/ReadOnlyProperty; public static final fun getClassId (Ljava/lang/Class;)Lorg/jetbrains/kotlin/name/ClassId; + public static final fun getContributesToAnnotation (Ljava/lang/Class;)Lcom/squareup/anvil/annotations/ContributesTo; public static final fun getDaggerComponent (Ljava/lang/Class;)Ldagger/Component; public static final fun getDaggerModule (Ljava/lang/Class;)Ldagger/Module; public static final fun getDaggerSubcomponent (Ljava/lang/Class;)Ldagger/Subcomponent; + public static final fun getGeneratedBindingModule (Ljava/lang/Class;)Ljava/lang/Class; public static final fun getMembersInjector (Ljava/lang/Class;)Ljava/lang/Class; public static final fun packageName (Ljava/lang/Class;)Ljava/lang/String; public static final fun use (Ljava/lang/reflect/AccessibleObject;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; @@ -477,7 +487,6 @@ public final class com/squareup/anvil/compiler/testing/reflect/ReflectionKt { public static final fun getAssistedService (Ljava/lang/ClassLoader;)Ljava/lang/Class; public static final fun getAssistedServiceFactory (Ljava/lang/ClassLoader;)Ljava/lang/Class; public static final fun getComponentInterface (Ljava/lang/ClassLoader;)Ljava/lang/Class; - public static final fun getContributesToAnnotation (Ljava/lang/Class;)Lcom/squareup/anvil/annotations/ContributesTo; public static final fun getContributingInterface (Ljava/lang/ClassLoader;)Ljava/lang/Class; public static final fun getContributingObject (Ljava/lang/ClassLoader;)Ljava/lang/Class; public static final fun getDaggerModule1 (Ljava/lang/ClassLoader;)Ljava/lang/Class; @@ -486,7 +495,6 @@ public final class com/squareup/anvil/compiler/testing/reflect/ReflectionKt { public static final fun getDaggerModule4 (Ljava/lang/ClassLoader;)Ljava/lang/Class; public static final fun getDeclaredFieldValue (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object; public static final fun getFieldValue (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object; - public static final fun getGeneratedBindingModule (Ljava/lang/Class;)Ljava/lang/Class; public static final fun getInjectClass (Ljava/lang/ClassLoader;)Ljava/lang/Class; public static final fun getInjectClass_Factory (Ljava/lang/ClassLoader;)Ljava/lang/Class; public static final fun getJavaClass (Ljava/lang/ClassLoader;)Ljava/lang/Class; diff --git a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/CompilationEnvironment.kt b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/CompilationEnvironment.kt index ecbbea259..680248162 100644 --- a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/CompilationEnvironment.kt +++ b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/CompilationEnvironment.kt @@ -7,7 +7,7 @@ import com.rickbusarow.kase.ParamTestEnvironmentFactory import com.rickbusarow.kase.TestEnvironment import com.rickbusarow.kase.asClueCatching import com.rickbusarow.kase.stdlib.createSafely -import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionFactory +import com.squareup.anvil.compiler.k2.fir.AnvilFirProcessor import com.squareup.anvil.compiler.testing.compilation.Compile2Compilation import com.squareup.anvil.compiler.testing.compilation.Compile2CompilationConfiguration import com.squareup.anvil.compiler.testing.compilation.Compile2Result @@ -49,7 +49,7 @@ public interface CompilationEnvironment : TestEnvironment { * * @param kotlinSources one or more Kotlin source strings * @param javaSources optional Java source strings - * @param firExtensions optional FIR extension factories for advanced compiler customization + * @param firProcessors optional FIR extension factories for advanced compiler customization * @param configuration an optional config transform to modify the default [Compile2CompilationConfiguration] * @param expectedExitCode automatically asserted against each compilation phase's result * @param exec invoked with the [Compile2Result] after compilation @@ -63,7 +63,7 @@ public interface CompilationEnvironment : TestEnvironment { public fun compile2( @Language("kotlin") vararg kotlinSources: String, javaSources: List = emptyList(), - firExtensions: List> = emptyList(), + firProcessors: List = emptyList(), configuration: (Compile2CompilationConfiguration) -> Compile2CompilationConfiguration = { it }, expectedExitCode: ExitCode = ExitCode.OK, previousCompilation: Compile2Result? = null, @@ -94,11 +94,12 @@ public interface CompilationEnvironment : TestEnvironment { return compile2( sourceFiles = kotlinFiles + javaFiles, - firExtensions = firExtensions, + firProcessors = firProcessors, configuration = configuration, expectedExitCode = expectedExitCode, previousCompilation = previousCompilation, mode = mode, + workingDir = workingDir, exec = exec, ) } @@ -116,9 +117,9 @@ public interface CompilationEnvironment : TestEnvironment { * utilities for scanning or packaging them (e.g., `jar` creation). * * @param sourceFiles a list of .kt or .java files to be compiled - * @param firExtensions optional FIR extension factories for custom processing + * @param firProcessors optional FIR extension factories for custom processing * @param configuration a lambda to customize the [Compile2CompilationConfiguration] before compilation - * @param firExtensions optional FIR extension factories for advanced compiler customization + * @param firProcessors optional FIR extension factories for advanced compiler customization * @param configuration an optional config transform to modify the default [Compile2CompilationConfiguration] * @param expectedExitCode automatically asserted against each compilation phase's result * @param previousCompilation a previous [Compile2Result] to add to the classpath for this compilation @@ -133,7 +134,7 @@ public interface CompilationEnvironment : TestEnvironment { */ public fun compile2( sourceFiles: List, - firExtensions: List> = emptyList(), + firProcessors: List = emptyList(), configuration: (Compile2CompilationConfiguration) -> Compile2CompilationConfiguration = { it }, expectedExitCode: ExitCode = ExitCode.OK, previousCompilation: Compile2Result? = null, @@ -144,7 +145,7 @@ public interface CompilationEnvironment : TestEnvironment { val config = Compile2CompilationConfiguration.default( sourceFiles = sourceFiles, - firExtensions = firExtensions, + firProcessors = firProcessors, workingDir = workingDir, useKapt = mode.useKapt, previousCompilation = previousCompilation, diff --git a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/classgraph/annotationInfo.kt b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/classgraph/annotationInfo.kt new file mode 100644 index 000000000..b1f85ebcb --- /dev/null +++ b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/classgraph/annotationInfo.kt @@ -0,0 +1,33 @@ +package com.squareup.anvil.compiler.testing.classgraph + +import com.squareup.anvil.compiler.k2.utils.names.Names +import io.github.classgraph.AnnotationClassRef +import io.github.classgraph.AnnotationInfo +import io.kotest.matchers.collections.shouldContain +import org.jetbrains.kotlin.name.Name + +/** Returns the fully qualified name of the Anvil scope argument */ +public val AnnotationInfo.scope: String + get() = requireParameterAs(Names.scope).name + +/** Returns all Dagger modules specified by the Component as fully qualified names */ +public val AnnotationInfo.moduleNames: List + get() = requireParameterAs>( + Names.modules, + ).map { (it as AnnotationClassRef).name }.sorted() + +/** Returns all Dagger modules specified by the Component as fully qualified names */ +public val AnnotationInfo.hints: List + get() = requireParameterAs>(Names.hints).sorted() + +/** Returns a value parameter named `name` from the annotation, or throws if it doesn't exist. */ +public fun AnnotationInfo.requireParameter(name: Name, includeDefaultValues: Boolean = true): Any { + getParameterValues(includeDefaultValues).names shouldContain name.identifier + return getParameterValues(includeDefaultValues).getValue(name.identifier) +} + +/** Returns a value parameter named `name` from the annotation, or throws if it doesn't exist. */ +public inline fun AnnotationInfo.requireParameterAs( + name: Name, + includeDefaultValues: Boolean = true, +): T = requireParameter(name, includeDefaultValues) as T diff --git a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/classgraph/classInfo.kt b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/classgraph/classInfo.kt index ac8a4c6d8..1e54ed8eb 100644 --- a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/classgraph/classInfo.kt +++ b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/classgraph/classInfo.kt @@ -1,8 +1,6 @@ package com.squareup.anvil.compiler.testing.classgraph -import com.squareup.anvil.compiler.k2.utils.names.Names import com.squareup.anvil.compiler.testing.asJavaNameString -import io.github.classgraph.AnnotationClassRef import io.github.classgraph.AnnotationInfo import io.github.classgraph.ClassInfo import org.jetbrains.kotlin.name.ClassId @@ -29,18 +27,6 @@ public fun ClassInfo.getAnnotationInfo(annotationClassId: ClassId): AnnotationIn return getAnnotationInfo(annotationClassId.asJavaNameString()) } -/** - * Returns all Dagger modules specified by the Component as fully qualified names - */ -public val AnnotationInfo.moduleNames: List - get() { - return parameterValues - .getValue(Names.modules.identifier) - .let { it as Array<*> } - .map { (it as AnnotationClassRef).name } - .sorted() - } - /** * Returns all binding methods declared in a particular `@Module` type. * The methods are sorted by their name. diff --git a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2.kt b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2.kt index c809659f1..9f20d73de 100644 --- a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2.kt +++ b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2.kt @@ -1,6 +1,8 @@ package com.squareup.anvil.compiler.testing.compilation import com.rickbusarow.kase.stdlib.div +import com.squareup.anvil.annotations.internal.InternalAnvilApi +import com.squareup.anvil.compiler.k2.fir.AdditionalProcessorsHolder import com.squareup.anvil.compiler.testing.CompilationEnvironment import dagger.internal.codegen.ComponentProcessor import io.kotest.matchers.shouldBe @@ -126,13 +128,10 @@ public class Compile2Compilation( parseCommandLineArguments(baseArgs.toArgumentStrings()) try { - if (config.firExtensions.isNotEmpty()) { - // Register the FIR extensions, if any, via thread-local. - Compile2CompilerPluginRegistrar.threadLocalParams.set( - Compile2CompilerPluginRegistrar.Compile2RegistrarParams( - firExtensionFactories = config.firExtensions, - ), - ) + if (config.firProcessors.isNotEmpty()) { + // Register the custom FIR processors, if any, via thread-local. + @OptIn(InternalAnvilApi::class) + AdditionalProcessorsHolder.additionalProcessors.set(config.firProcessors) } // KAPT invocation happens first, generating stubs and Java/Kotlin sources if necessary. @@ -184,7 +183,8 @@ public class Compile2Compilation( compilerMessages = messageRenderer.messages(), ) } finally { - Compile2CompilerPluginRegistrar.threadLocalParams.remove() + @OptIn(InternalAnvilApi::class) + AdditionalProcessorsHolder.additionalProcessors.remove() } } diff --git a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2CompilationConfiguration.kt b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2CompilationConfiguration.kt index 084008736..ce3b1b16f 100644 --- a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2CompilationConfiguration.kt +++ b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2CompilationConfiguration.kt @@ -2,7 +2,7 @@ package com.squareup.anvil.compiler.testing.compilation import com.rickbusarow.kase.stdlib.div import com.rickbusarow.kase.stdlib.letIf -import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionFactory +import com.squareup.anvil.compiler.k2.fir.AnvilFirProcessor import com.squareup.anvil.compiler.testing.BuildConfig import com.squareup.anvil.compiler.testing.CompilationMode import org.jetbrains.kotlin.config.LanguageVersion @@ -29,7 +29,7 @@ import java.io.File * @property compilationClasspath Classpath files required for compilation. * @property compilerPluginClasspath Classpath files for any compiler plugins (including Anvil). * @property kaptPluginClasspath Classpath files for KAPT annotation processors (if [useKapt] is true). - * @property firExtensions Optional FIR plugin extensions for one-off compiler tests. + * @property firProcessors Optional FIR plugin extensions for one-off compiler tests. */ public data class Compile2CompilationConfiguration( val rootDir: File, @@ -50,7 +50,7 @@ public data class Compile2CompilationConfiguration( val compilationClasspath: List, val compilerPluginClasspath: List, val kaptPluginClasspath: List, - val firExtensions: List>, + val firProcessors: List, ) { public companion object { @@ -60,7 +60,7 @@ public data class Compile2CompilationConfiguration( * suitable for typical test usage. * * @param sourceFiles a list of .kt or .java files to be compiled - * @param firExtensions optional FIR extension factories for custom processing + * @param firProcessors optional FIR extension factories for custom processing * @param workingDir The directory to serve as [rootDir]. * @param useKapt Whether to enable KAPT support in the resulting configuration. * @param previousCompilation a previous [Compile2Result] to add to the classpath for this compilation @@ -68,7 +68,7 @@ public data class Compile2CompilationConfiguration( */ public fun default( sourceFiles: List, - firExtensions: List>, + firProcessors: List, workingDir: File, useKapt: Boolean, mode: CompilationMode, @@ -105,7 +105,7 @@ public data class Compile2CompilationConfiguration( }, jdkHome = javaHomeOrNull(), moduleName = workingDir.name, - firExtensions = firExtensions, + firProcessors = firProcessors, // Classpath for the compiler itself. compilationClasspath = compilationClasspath, diff --git a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2CompilerPluginRegistrar.kt b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2CompilerPluginRegistrar.kt deleted file mode 100644 index d8a916bf1..000000000 --- a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2CompilerPluginRegistrar.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.squareup.anvil.compiler.testing.compilation - -import com.google.auto.service.AutoService -import com.squareup.anvil.compiler.k2.fir.AnvilFirContext -import com.squareup.anvil.compiler.k2.fir.AnvilFirDeclarationGenerationExtension -import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionFactory -import com.squareup.anvil.compiler.k2.fir.AnvilFirExtensionSessionComponent -import com.squareup.anvil.compiler.k2.fir.AnvilFirSupertypeGenerationExtension -import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.config.messageCollector -import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar -import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter - -@AutoService(CompilerPluginRegistrar::class) -internal class Compile2CompilerPluginRegistrar : CompilerPluginRegistrar() { - override val supportsK2: Boolean - get() = true - - override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { - val factories = threadLocalParams.get()?.firExtensionFactories - - if (!factories.isNullOrEmpty()) { - FirExtensionRegistrarAdapter.registerExtension( - Compile2FirExtensionRegistrar( - messageCollector = configuration.messageCollector, - factories = factories, - ), - ) - } - } - - companion object { - internal val threadLocalParams = ThreadLocal() - } - - data class Compile2RegistrarParams( - val firExtensionFactories: List>, - ) -} - -public class Compile2FirExtensionRegistrar( - private val messageCollector: MessageCollector, - private val factories: List>, -) : FirExtensionRegistrar() { - - override fun ExtensionRegistrarContext.configurePlugin() { - - val ctx = AnvilFirContext(messageCollector) - - for (factory in factories) { - - when (factory) { - is AnvilFirDeclarationGenerationExtension.Factory -> factory.create(ctx).unaryPlus() - is AnvilFirSupertypeGenerationExtension.Factory -> factory.create(ctx).unaryPlus() - is AnvilFirExtensionSessionComponent.Factory -> factory.create(ctx).unaryPlus() - } - } - } -} diff --git a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2Result.kt b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2Result.kt index 9da0e36d3..133b83cbc 100644 --- a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2Result.kt +++ b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2Result.kt @@ -67,7 +67,7 @@ public class Compile2Result( * @see classLoader for another way to load java Class types for introspection. */ public val scanResult: ScanResult by lazy(LazyThreadSafetyMode.NONE) { - classGraphResult(classpathFiles + jar) + classGraphResult(classpathFiles.filter { it.isJarFile() } + jar) } @Deprecated("use scanResult", ReplaceWith("scanResult")) @@ -113,6 +113,11 @@ public fun File.requireIsJarFile(): File = apply { require(extension == "jar") { "Expected a .jar file, but was: $this" } } +/** + * @return `true` if the receiver is an existing file and has a `.jar` extension, otherwise `false` + */ +public fun File.isJarFile(): Boolean = isFile && extension == "jar" + /** * Creates a [ScanResult] by scanning all provided [jars] with [ClassGraph]. * Any attempt to scan non-jar files results in an error due to [requireIsJarFile]. diff --git a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/reflect/class.kt b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/reflect/class.kt index bff66b44c..a89faf635 100644 --- a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/reflect/class.kt +++ b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/reflect/class.kt @@ -1,9 +1,12 @@ package com.squareup.anvil.compiler.testing.reflect +import com.squareup.anvil.annotations.ContributesTo import com.squareup.anvil.annotations.ExperimentalAnvilApi import com.squareup.anvil.annotations.internal.InternalBindingMarker +import com.squareup.anvil.compiler.k2.utils.names.bindingModuleSibling import com.squareup.anvil.compiler.k2.utils.names.factoryJoined import com.squareup.anvil.compiler.k2.utils.names.membersInjectorSibling +import com.squareup.anvil.compiler.testing.classId import dagger.Component import dagger.Module import dagger.Subcomponent @@ -16,6 +19,12 @@ import java.lang.reflect.Field import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KClass +public val Class<*>.generatedBindingModule: Class<*> + get() = classLoader.loadClass(kotlin.classId.bindingModuleSibling) + +public val Class<*>.contributesToAnnotation: ContributesTo + get() = annotations.filterIsInstance().single() + @ExperimentalAnvilApi public val Class<*>.membersInjector: Class<*> get() = classLoader.loadClass(classId.membersInjectorSibling) diff --git a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/reflect/reflection.kt b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/reflect/reflection.kt index 88364308b..bae6e4025 100644 --- a/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/reflect/reflection.kt +++ b/compiler-testing/src/main/kotlin/com/squareup/anvil/compiler/testing/reflect/reflection.kt @@ -1,6 +1,5 @@ package com.squareup.anvil.compiler.testing.reflect -import com.squareup.anvil.annotations.ContributesTo import com.squareup.anvil.annotations.ExperimentalAnvilApi import com.squareup.anvil.compiler.testing.TestNames import com.squareup.anvil.compiler.testing.asJavaNameString @@ -87,8 +86,3 @@ public val ClassLoader.javaClass: Class<*> get() = loadClass(TestNames.javaClass) public val ClassLoader.anyQualifier: Class<*> get() = loadClass(TestNames.anyQualifier) - -public val Class<*>.generatedBindingModule: Class<*> - get() = classLoader.loadClass("${kotlin.qualifiedName}BindingModule") -public val Class<*>.contributesToAnnotation: ContributesTo - get() = annotations.filterIsInstance().single() diff --git a/compiler-testing/src/test/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2Sample.kt b/compiler-testing/src/test/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2Sample.kt index 4fff7eb4b..edd2281ef 100644 --- a/compiler-testing/src/test/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2Sample.kt +++ b/compiler-testing/src/test/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2Sample.kt @@ -1,6 +1,8 @@ package com.squareup.anvil.compiler.testing.compilation -import com.squareup.anvil.compiler.k2.fir.AnvilFirSupertypeGenerationExtension +import com.squareup.anvil.compiler.k2.fir.AnvilFirContext +import com.squareup.anvil.compiler.k2.fir.AnvilFirProcessor +import com.squareup.anvil.compiler.k2.fir.SupertypeProcessor import com.squareup.anvil.compiler.testing.CompilationModeTest import com.squareup.anvil.compiler.testing.TestNames import com.squareup.anvil.compiler.testing.classgraph.injectClass @@ -52,29 +54,27 @@ class Compile2Sample : CompilationModeTest() { class InjectClass """, - firExtensions = listOf(myCustomGenerator()), + firProcessors = listOf(myCustomGenerator()), ) { scanResult.injectClass.interfaces.names shouldBe listOf("com.squareup.test.ParentInterface") } } - private fun myCustomGenerator(): AnvilFirSupertypeGenerationExtension.Factory = - AnvilFirSupertypeGenerationExtension.Factory { ctx -> - FirSupertypeGenerationExtension.Factory { session -> - - object : AnvilFirSupertypeGenerationExtension(ctx, session) { - override fun needTransformSupertypes( - declaration: FirClassLikeDeclaration, - ): Boolean = declaration.classId == TestNames.injectClass + private fun myCustomGenerator(): AnvilFirProcessor.Factory = + AnvilFirProcessor.Factory { ctx -> + object : SupertypeProcessor() { + override val anvilContext: AnvilFirContext = ctx + override fun shouldProcess(declaration: FirClassLikeDeclaration): Boolean { + return declaration.classId == TestNames.injectClass + } - override fun computeAdditionalSupertypes( - classLikeDeclaration: FirClassLikeDeclaration, - resolvedSupertypes: List, - typeResolver: TypeResolveService, - ): List = listOf( - TestNames.parentInterface.constructClassLikeType(), - ) + override fun addSupertypes( + classLikeDeclaration: FirClassLikeDeclaration, + resolvedSupertypes: List, + typeResolver: FirSupertypeGenerationExtension.TypeResolveService, + ): List { + return listOf(TestNames.parentInterface.constructClassLikeType()) } } } diff --git a/compiler-testing/src/test/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2Test.kt b/compiler-testing/src/test/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2Test.kt index 1615012ec..fa925a47a 100644 --- a/compiler-testing/src/test/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2Test.kt +++ b/compiler-testing/src/test/kotlin/com/squareup/anvil/compiler/testing/compilation/Compile2Test.kt @@ -1,7 +1,9 @@ package com.squareup.anvil.compiler.testing.compilation import com.rickbusarow.kase.stdlib.div -import com.squareup.anvil.compiler.k2.fir.AnvilFirSupertypeGenerationExtension +import com.squareup.anvil.compiler.k2.fir.AnvilFirContext +import com.squareup.anvil.compiler.k2.fir.AnvilFirProcessor +import com.squareup.anvil.compiler.k2.fir.SupertypeProcessor import com.squareup.anvil.compiler.testing.CompilationModeTest import com.squareup.anvil.compiler.testing.TestNames import com.squareup.anvil.compiler.testing.classgraph.classIds @@ -122,23 +124,19 @@ class Compile2Test : CompilationModeTest() { class InjectClass """, - firExtensions = listOf( - AnvilFirSupertypeGenerationExtension.Factory { ctx -> - FirSupertypeGenerationExtension.Factory { session -> - - object : AnvilFirSupertypeGenerationExtension(ctx, session) { - override fun needTransformSupertypes( - declaration: FirClassLikeDeclaration, - ): Boolean = declaration.classId == TestNames.injectClass - - override fun computeAdditionalSupertypes( - classLikeDeclaration: FirClassLikeDeclaration, - resolvedSupertypes: List, - typeResolver: TypeResolveService, - ): List = listOf( - TestNames.parentInterface.constructClassLikeType(), - ) + firProcessors = listOf( + AnvilFirProcessor.Factory { ctx -> + object : SupertypeProcessor() { + override val anvilContext: AnvilFirContext = ctx + override fun shouldProcess(declaration: FirClassLikeDeclaration): Boolean { + return declaration.classId == TestNames.injectClass } + + override fun addSupertypes( + classLikeDeclaration: FirClassLikeDeclaration, + resolvedSupertypes: List, + typeResolver: FirSupertypeGenerationExtension.TypeResolveService, + ): List = listOf(TestNames.parentInterface.constructClassLikeType()) } }, ),