Skip to content

Commit

Permalink
Initial support for Jakarta annotations (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZacSweers authored Oct 24, 2024
1 parent 3e46507 commit 36264e7
Show file tree
Hide file tree
Showing 17 changed files with 116 additions and 61 deletions.
6 changes: 6 additions & 0 deletions compiler-utils/api/compiler-utils.api
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,17 @@ public final class com/squareup/anvil/compiler/internal/ksp/KspUtilKt {
public static final fun getFqName (Lcom/google/devtools/ksp/symbol/KSClassDeclaration;)Lorg/jetbrains/kotlin/name/FqName;
public static final fun getFqName (Lcom/google/devtools/ksp/symbol/KSType;)Lorg/jetbrains/kotlin/name/FqName;
public static final fun getKSAnnotationsByQualifiedName (Lcom/google/devtools/ksp/symbol/KSAnnotated;Ljava/lang/String;)Lkotlin/sequences/Sequence;
public static final fun getKSAnnotationsByQualifiedName (Lcom/google/devtools/ksp/symbol/KSAnnotated;Ljava/util/Set;)Lkotlin/sequences/Sequence;
public static final fun getKSAnnotationsByType (Lcom/google/devtools/ksp/symbol/KSAnnotated;Lkotlin/reflect/KClass;)Lkotlin/sequences/Sequence;
public static final fun getReportableReturnTypeNode (Lcom/google/devtools/ksp/symbol/KSFunctionDeclaration;)Lcom/google/devtools/ksp/symbol/KSNode;
public static final fun getResolvableAnnotations (Lcom/google/devtools/ksp/symbol/KSAnnotated;)Lkotlin/sequences/Sequence;
public static final fun getSymbolsWithAnnotations (Lcom/google/devtools/ksp/processing/Resolver;Ljava/util/Set;)Lkotlin/sequences/Sequence;
public static final fun injectConstructors (Lcom/google/devtools/ksp/processing/Resolver;)Ljava/util/List;
public static final fun isAnnotationClass (Lcom/google/devtools/ksp/symbol/KSClassDeclaration;)Z
public static final fun isAnnotationPresent (Lcom/google/devtools/ksp/symbol/KSAnnotated;Ljava/lang/String;)Z
public static final fun isAnnotationPresent (Lcom/google/devtools/ksp/symbol/KSAnnotated;Ljava/util/Set;)Z
public static final fun isAnnotationPresent (Lcom/google/devtools/ksp/symbol/KSAnnotated;Lkotlin/reflect/KClass;)Z
public static final fun isAnnotationPresentFqName (Lcom/google/devtools/ksp/symbol/KSAnnotated;Ljava/util/Set;)Z
public static final fun isExtensionDeclaration (Lcom/google/devtools/ksp/symbol/KSFunctionDeclaration;)Z
public static final fun isLateInit (Lcom/google/devtools/ksp/symbol/KSModifierListOwner;)Z
public static final fun mergeAnnotations (Lcom/google/devtools/ksp/symbol/KSAnnotated;)Ljava/util/List;
Expand All @@ -159,10 +163,12 @@ public final class com/squareup/anvil/compiler/internal/ksp/KspUtilKt {

public abstract interface class com/squareup/anvil/compiler/internal/reference/AnnotatedReference {
public abstract fun getAnnotations ()Ljava/util/List;
public fun isAnnotatedWith (Ljava/util/Set;)Z
public fun isAnnotatedWith (Lorg/jetbrains/kotlin/name/FqName;)Z
}

public final class com/squareup/anvil/compiler/internal/reference/AnnotatedReference$DefaultImpls {
public static fun isAnnotatedWith (Lcom/squareup/anvil/compiler/internal/reference/AnnotatedReference;Ljava/util/Set;)Z
public static fun isAnnotatedWith (Lcom/squareup/anvil/compiler/internal/reference/AnnotatedReference;Lorg/jetbrains/kotlin/name/FqName;)Z
}

Expand Down
3 changes: 2 additions & 1 deletion compiler-utils/dependencies/runtimeClasspath.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
com.google.dagger:dagger:2.51.1
com.google.dagger:dagger:2.52
com.google.devtools.ksp:symbol-processing-api:1.9.24-1.0.20
com.squareup:kotlinpoet-jvm:1.16.0
com.squareup:kotlinpoet-ksp:1.16.0
com.squareup:kotlinpoet:1.16.0
jakarta.inject:jakarta.inject-api:2.0.1
javax.inject:javax.inject:1
org.jetbrains.intellij.deps:trove4j:1.0.20200330
org.jetbrains.kotlin:kotlin-bom:1.9.24
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,22 @@ import javax.inject.Inject
import javax.inject.Qualifier
import javax.inject.Scope
import kotlin.reflect.KClass
import jakarta.inject.Qualifier as JakartaQualifier
import jakarta.inject.Scope as JakartaScope

internal val jvmSuppressWildcardsFqName = JvmSuppressWildcards::class.fqName
internal val publishedApiFqName = PublishedApi::class.fqName
internal val qualifierFqName = Qualifier::class.fqName
internal val qualifierFqNames = setOf(
Qualifier::class.fqName,
JakartaQualifier::class.fqName,
)
internal val mapKeyFqName = MapKey::class.fqName
internal val daggerProvidesFqName = Provides::class.fqName
internal val daggerLazyFqName = Lazy::class.fqName
internal val daggerScopeFqName = Scope::class.fqName
internal val scopeFqNames = setOf(
Scope::class.fqName,
JakartaScope::class.fqName,
)
internal val injectFqName = Inject::class.fqName

internal val contributesToFqName = ContributesTo::class.fqName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSType
import com.google.devtools.ksp.symbol.KSValueArgument
import com.squareup.anvil.annotations.ContributesBinding
import com.squareup.anvil.compiler.internal.daggerScopeFqName
import com.squareup.anvil.compiler.internal.mapKeyFqName
import com.squareup.anvil.compiler.internal.qualifierFqName
import com.squareup.anvil.compiler.internal.qualifierFqNames
import com.squareup.anvil.compiler.internal.scopeFqNames
import com.squareup.kotlinpoet.ClassName
import org.jetbrains.kotlin.name.FqName

Expand Down Expand Up @@ -207,14 +207,18 @@ public fun KSAnnotation.argumentAt(
}

private fun KSAnnotation.isTypeAnnotatedWith(
annotationFqName: FqName,
vararg annotationFqName: FqName,
): Boolean = isTypeAnnotatedWith(annotationFqName.toSet())

private fun KSAnnotation.isTypeAnnotatedWith(
annotationFqNames: Set<FqName>,
): Boolean = annotationType.resolve()
.declaration
.isAnnotationPresent(annotationFqName.asString())
.isAnnotationPresent(annotationFqNames.toSet())

public fun KSAnnotation.isQualifier(): Boolean = isTypeAnnotatedWith(qualifierFqName)
public fun KSAnnotation.isQualifier(): Boolean = isTypeAnnotatedWith(qualifierFqNames)
public fun KSAnnotation.isMapKey(): Boolean = isTypeAnnotatedWith(mapKeyFqName)
public fun KSAnnotation.isDaggerScope(): Boolean = isTypeAnnotatedWith(daggerScopeFqName)
public fun KSAnnotation.isDaggerScope(): Boolean = isTypeAnnotatedWith(scopeFqNames)

public fun KSAnnotated.qualifierAnnotation(): KSAnnotation? =
resolvableAnnotations.singleOrNull { it.isQualifier() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ public fun <T : Annotation> KSAnnotated.getKSAnnotationsByType(
*/
public fun KSAnnotated.getKSAnnotationsByQualifiedName(
qualifiedName: String,
): Sequence<KSAnnotation> {
return getKSAnnotationsByQualifiedName(setOf(qualifiedName))
}

/**
* Returns a sequence of [KSAnnotations][KSAnnotation] of the given [qualifiedName].
*/
public fun KSAnnotated.getKSAnnotationsByQualifiedName(
qualifiedNames: Set<String>,
): Sequence<KSAnnotation> {
// Don't use resolvableAnnotations here to save the double resolve() call
return annotations.filter {
Expand All @@ -72,13 +81,20 @@ public fun KSAnnotated.getKSAnnotationsByQualifiedName(
if (type.isError) return@filter false

// Resolve the KSClassDeclaration to ensure we peek through typealiases
type.resolveKSClassDeclaration()?.qualifiedName?.asString() == qualifiedName
type.resolveKSClassDeclaration()?.qualifiedName?.asString() in qualifiedNames
}
}

public fun KSAnnotated.isAnnotationPresent(qualifiedName: String): Boolean =
getKSAnnotationsByQualifiedName(qualifiedName).firstOrNull() != null

@JvmName("isAnnotationPresentFqName")
public fun KSAnnotated.isAnnotationPresent(qualifiedNames: Set<FqName>): Boolean =
isAnnotationPresent(qualifiedNames.mapTo(mutableSetOf()) { it.asString() })

public fun KSAnnotated.isAnnotationPresent(qualifiedNames: Set<String>): Boolean =
getKSAnnotationsByQualifiedName(qualifiedNames).firstOrNull() != null

public inline fun <reified T> KSAnnotated.isAnnotationPresent(): Boolean {
return isAnnotationPresent(T::class)
}
Expand Down Expand Up @@ -478,3 +494,7 @@ public fun Resolver.requireClassDeclaration(fqName: FqName, node: KSNode?): KSCl
}
}
}

public fun Resolver.getSymbolsWithAnnotations(fqNames: Set<FqName>): Sequence<KSAnnotated> {
return fqNames.asSequence().flatMap { getSymbolsWithAnnotation(it.asString()) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ public interface AnnotatedReference {
public val annotations: List<AnnotationReference>

public fun isAnnotatedWith(fqName: FqName): Boolean = annotations.any { it.fqName == fqName }
public fun isAnnotatedWith(
fqNames: Set<FqName>,
): Boolean = annotations.any { it.fqName in fqNames }
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import com.squareup.anvil.compiler.internal.contributesBindingFqName
import com.squareup.anvil.compiler.internal.contributesMultibindingFqName
import com.squareup.anvil.compiler.internal.contributesSubcomponentFqName
import com.squareup.anvil.compiler.internal.contributesToFqName
import com.squareup.anvil.compiler.internal.daggerScopeFqName
import com.squareup.anvil.compiler.internal.mapKeyFqName
import com.squareup.anvil.compiler.internal.mergeComponentFqName
import com.squareup.anvil.compiler.internal.mergeInterfacesFqName
import com.squareup.anvil.compiler.internal.mergeModulesFqName
import com.squareup.anvil.compiler.internal.mergeSubcomponentFqName
import com.squareup.anvil.compiler.internal.qualifierFqName
import com.squareup.anvil.compiler.internal.qualifierFqNames
import com.squareup.anvil.compiler.internal.reference.AnnotationReference.Descriptor
import com.squareup.anvil.compiler.internal.reference.AnnotationReference.Psi
import com.squareup.anvil.compiler.internal.requireFqName
import com.squareup.anvil.compiler.internal.scopeFqNames
import com.squareup.kotlinpoet.AnnotationSpec
import com.squareup.kotlinpoet.MemberName
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
Expand Down Expand Up @@ -87,9 +87,9 @@ public sealed class AnnotationReference {
public fun exclude(parameterIndex: Int = excludeIndex(fqName)): List<ClassReference> =
argumentAt("exclude", parameterIndex)?.value<List<ClassReference>>().orEmpty()

public fun isQualifier(): Boolean = classReference.isAnnotatedWith(qualifierFqName)
public fun isQualifier(): Boolean = classReference.isAnnotatedWith(qualifierFqNames)
public fun isMapKey(): Boolean = classReference.isAnnotatedWith(mapKeyFqName)
public fun isDaggerScope(): Boolean = classReference.isAnnotatedWith(daggerScopeFqName)
public fun isDaggerScope(): Boolean = classReference.isAnnotatedWith(scopeFqNames)

public fun toAnnotationSpec(): AnnotationSpec {
return AnnotationSpec
Expand Down
1 change: 1 addition & 0 deletions compiler/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ dependencies {
implementation(platform(libs.kotlin.bom))
implementation(libs.dagger2)
implementation(libs.jsr250)
implementation(libs.jakarta)
implementation(libs.kotlinpoet)
implementation(libs.kotlinpoet.ksp)

Expand Down
3 changes: 2 additions & 1 deletion compiler/dependencies/runtimeClasspath.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
com.google.dagger:dagger:2.51.1
com.google.dagger:dagger:2.52
com.google.devtools.ksp:symbol-processing-api:1.9.24-1.0.20
com.squareup:kotlinpoet-jvm:1.16.0
com.squareup:kotlinpoet-ksp:1.16.0
com.squareup:kotlinpoet:1.16.0
jakarta.inject:jakarta.inject-api:2.0.1
javax.annotation:jsr250-api:1.0
javax.inject:javax.inject:1
org.jetbrains.intellij.deps:trove4j:1.0.20200330
Expand Down
20 changes: 15 additions & 5 deletions compiler/src/main/java/com/squareup/anvil/compiler/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import dagger.internal.DoubleCheck
import java.io.File
import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Qualifier
import dagger.internal.Provider as DaggerProvider
import jakarta.inject.Inject as JakartaInject
import jakarta.inject.Provider as JakartaProvider

internal val mergeComponentFqName = MergeComponent::class.fqName
internal val mergeComponentFactoryFqName = MergeComponent.Factory::class.fqName
Expand Down Expand Up @@ -69,14 +71,22 @@ internal val daggerBindsFqName = Binds::class.fqName
internal val daggerProvidesFqName = Provides::class.fqName
internal val daggerLazyFqName = Lazy::class.fqName
internal val daggerLazyClassName = Lazy::class.asClassName()
internal val injectFqName = Inject::class.fqName
internal val qualifierFqName = Qualifier::class.fqName
internal val injectFqNames = setOf(
Inject::class.fqName,
JakartaInject::class.fqName,
)
internal val mapKeyFqName = MapKey::class.fqName
internal val assistedFqName = Assisted::class.fqName
internal val assistedFactoryFqName = AssistedFactory::class.fqName
internal val assistedInjectFqName = AssistedInject::class.fqName
internal val providerFqName = Provider::class.fqName
internal val providerClassName = Provider::class.asClassName()
internal val daggerProviderClassName = DaggerProvider::class.asClassName()
internal val javaxProviderClassName = Provider::class.asClassName()
internal val providerClassNames = setOf(
Provider::class.asClassName(),
JakartaProvider::class.asClassName(),
DaggerProvider::class.asClassName(),
)
internal val providerFqNames = providerClassNames.mapToSet { it.fqName }
internal val jvmSuppressWildcardsFqName = JvmSuppressWildcards::class.fqName
internal val jvmFieldFqName = JvmField::class.fqName
internal val publishedApiFqName = PublishedApi::class.fqName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.squareup.anvil.compiler.codegen
import com.squareup.anvil.compiler.anyFqName
import com.squareup.anvil.compiler.assistedInjectFqName
import com.squareup.anvil.compiler.contributesMultibindingFqName
import com.squareup.anvil.compiler.injectFqName
import com.squareup.anvil.compiler.injectFqNames
import com.squareup.anvil.compiler.internal.reference.AnnotationReference
import com.squareup.anvil.compiler.internal.reference.AnvilCompilationExceptionClassReference
import com.squareup.anvil.compiler.internal.reference.ClassReference
Expand Down Expand Up @@ -116,7 +116,7 @@ internal fun ClassReference.atLeastOneAnnotation(
*/
internal fun <T : MemberFunctionReference> Collection<T>.injectConstructor(): T? {
val constructors = filter {
it.isAnnotatedWith(injectFqName) || it.isAnnotatedWith(assistedInjectFqName)
it.isAnnotatedWith(injectFqNames) || it.isAnnotatedWith(assistedInjectFqName)
}

return when (constructors.size) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ import dagger.internal.InstanceFactory
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.psi.KtFile
import java.io.File
import javax.inject.Provider

internal object AssistedFactoryCodeGen : AnvilApplicabilityChecker {

Expand Down Expand Up @@ -591,7 +590,7 @@ internal object AssistedFactoryCodeGen : AnvilApplicabilityChecker {
.build()
}
TypeSpec.companionObjectBuilder()
.addFunction(createFactory("create", Provider::class.asClassName()))
.addFunction(createFactory("create", javax.inject.Provider::class.asClassName()))
// New in Dagger 2.50: factories for dagger.internal.Provider
.addFunction(
createFactory("createFactoryProvider", dagger.internal.Provider::class.asClassName()),
Expand Down
Loading

0 comments on commit 36264e7

Please sign in to comment.