Skip to content

Commit

Permalink
Make Dagger recognize Jakarta versions of Inject, Scope, Qualifier, a…
Browse files Browse the repository at this point in the history
…nd Singleton. This does not get rid of the runtime dependency nor does it support Provider yet.

Issue #2058.

RELNOTES=Support Jakarta versions of Inject, Scope, Qualifier, and Singleton
PiperOrigin-RevId: 424427709
  • Loading branch information
Chang-Eric authored and Dagger Team committed Jan 26, 2022
1 parent 3342e55 commit 2195513
Show file tree
Hide file tree
Showing 16 changed files with 200 additions and 50 deletions.
1 change: 1 addition & 0 deletions WORKSPACE
Expand Up @@ -242,6 +242,7 @@ maven_install(
"io.grpc:grpc-core:%s" % GRPC_VERSION,
"io.grpc:grpc-netty:%s" % GRPC_VERSION,
"io.grpc:grpc-protobuf:%s" % GRPC_VERSION,
"jakarta.inject:jakarta.inject-api:2.0.1",
"javax.annotation:jsr250-api:1.0",
"javax.inject:javax.inject:1",
"javax.inject:javax.inject-tck:1",
Expand Down
11 changes: 7 additions & 4 deletions java/dagger/android/processor/AndroidInjectorDescriptor.java
Expand Up @@ -25,6 +25,7 @@
import com.google.auto.common.MoreTypes;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
Expand Down Expand Up @@ -126,13 +127,15 @@ Optional<AndroidInjectorDescriptor> createIfValid(ExecutableElement method) {
}
}

for (AnnotationMirror scope
: getAnnotatedAnnotations(method, TypeNames.SCOPE)) {
for (AnnotationMirror scope : Sets.union(
getAnnotatedAnnotations(method, TypeNames.SCOPE),
getAnnotatedAnnotations(method, TypeNames.SCOPE_JAVAX))) {
builder.scopesBuilder().add(AnnotationSpec.get(scope));
}

for (AnnotationMirror qualifier
: getAnnotatedAnnotations(method, TypeNames.QUALIFIER)) {
for (AnnotationMirror qualifier : Sets.union(
getAnnotatedAnnotations(method, TypeNames.QUALIFIER),
getAnnotatedAnnotations(method, TypeNames.QUALIFIER_JAVAX))) {
reporter.reportError(
"@ContributesAndroidInjector methods cannot have qualifiers", qualifier);
}
Expand Down
7 changes: 5 additions & 2 deletions java/dagger/android/processor/AndroidMapKeyValidator.java
Expand Up @@ -26,6 +26,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Sets;
import com.squareup.javapoet.ClassName;
import dagger.MapKey;
import javax.annotation.processing.Messager;
Expand Down Expand Up @@ -78,7 +79,8 @@ public ImmutableSet<Element> process(ImmutableSetMultimap<String, Element> eleme
}

private void validateMethod(String annotation, ExecutableElement method) {
if (!getAnnotatedAnnotations(method, TypeNames.QUALIFIER).isEmpty()) {
if (!Sets.union(getAnnotatedAnnotations(method, TypeNames.QUALIFIER),
getAnnotatedAnnotations(method, TypeNames.QUALIFIER_JAVAX)).isEmpty()) {
return;
}

Expand All @@ -88,7 +90,8 @@ private void validateMethod(String annotation, ExecutableElement method) {
return;
}

if (!getAnnotatedAnnotations(method, TypeNames.SCOPE).isEmpty()) {
if (!Sets.union(getAnnotatedAnnotations(method, TypeNames.SCOPE),
getAnnotatedAnnotations(method, TypeNames.SCOPE_JAVAX)).isEmpty()) {
SuppressWarnings suppressedWarnings = method.getAnnotation(SuppressWarnings.class);
if (suppressedWarnings == null
|| !ImmutableSet.copyOf(suppressedWarnings.value())
Expand Down
6 changes: 4 additions & 2 deletions java/dagger/android/processor/TypeNames.java
Expand Up @@ -45,8 +45,10 @@ public final class TypeNames {

// Other classnames
public static final ClassName PROVIDER = ClassName.get("javax.inject", "Provider");
public static final ClassName QUALIFIER = ClassName.get("javax.inject", "Qualifier");
public static final ClassName SCOPE = ClassName.get("javax.inject", "Scope");
public static final ClassName QUALIFIER = ClassName.get("jakarta.inject", "Qualifier");
public static final ClassName QUALIFIER_JAVAX = ClassName.get("javax.inject", "Qualifier");
public static final ClassName SCOPE = ClassName.get("jakarta.inject", "Scope");
public static final ClassName SCOPE_JAVAX = ClassName.get("javax.inject", "Scope");

private TypeNames() {}
}
2 changes: 1 addition & 1 deletion java/dagger/internal/codegen/InjectProcessingStep.java
Expand Up @@ -51,7 +51,7 @@ final class InjectProcessingStep extends TypeCheckingProcessingStep<XElement> {

@Override
public ImmutableSet<ClassName> annotationClassNames() {
return ImmutableSet.of(TypeNames.INJECT, TypeNames.ASSISTED_INJECT);
return ImmutableSet.of(TypeNames.INJECT, TypeNames.INJECT_JAVAX, TypeNames.ASSISTED_INJECT);
}

@Override
Expand Down
4 changes: 3 additions & 1 deletion java/dagger/internal/codegen/base/Scopes.java
Expand Up @@ -24,6 +24,7 @@
import androidx.room.compiler.processing.XElement;
import androidx.room.compiler.processing.XProcessingEnv;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.spi.model.DaggerAnnotation;
import dagger.spi.model.Scope;
Expand Down Expand Up @@ -57,7 +58,8 @@ public static String getReadableSource(Scope scope) {

/** Returns all of the associated scopes for a source code element. */
public static ImmutableSet<Scope> scopesOf(XElement element) {
return getAnnotatedAnnotations(element, TypeNames.SCOPE).stream()
return Sets.union(getAnnotatedAnnotations(element, TypeNames.SCOPE),
getAnnotatedAnnotations(element, TypeNames.SCOPE_JAVAX)).stream()
.map(DaggerAnnotation::from)
.map(Scope::scope)
.collect(toImmutableSet());
Expand Down
4 changes: 1 addition & 3 deletions java/dagger/internal/codegen/binding/BindingFactory.java
Expand Up @@ -112,9 +112,7 @@ public final class BindingFactory {
// TODO(dpb): See if we can just pass the parameterized type and not also the constructor.
public ProvisionBinding injectionBinding(
XConstructorElement constructorElement, Optional<XType> resolvedEnclosingType) {
checkArgument(
constructorElement.hasAnnotation(TypeNames.INJECT)
|| constructorElement.hasAnnotation(TypeNames.ASSISTED_INJECT));
checkArgument(InjectionAnnotations.hasInjectOrAssistedInjectAnnotation(constructorElement));

XConstructorType constructorType = constructorElement.getExecutableType();
XType enclosingType = constructorElement.getEnclosingElement().getType();
Expand Down
67 changes: 51 additions & 16 deletions java/dagger/internal/codegen/binding/InjectionAnnotations.java
Expand Up @@ -34,7 +34,7 @@
import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static dagger.internal.codegen.langmodel.DaggerElements.getAnnotationMirror;
import static dagger.internal.codegen.langmodel.DaggerElements.isAnnotationPresent;
import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
import static dagger.internal.codegen.xprocessing.XElements.asMethodParameter;
import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
import static javax.lang.model.element.Modifier.STATIC;
Expand All @@ -53,7 +53,9 @@
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.squareup.javapoet.ClassName;
import dagger.internal.codegen.base.Scopes;
import dagger.internal.codegen.extension.DaggerCollectors;
Expand All @@ -64,6 +66,7 @@
import dagger.spi.model.DaggerAnnotation;
import dagger.spi.model.Scope;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import javax.inject.Inject;
import javax.lang.model.element.AnnotationMirror;
Expand Down Expand Up @@ -109,7 +112,7 @@ public Optional<Scope> getScope(XConstructorElement injectConstructor) {
* @throws IllegalArgumentException if the given constructor is not an inject constructor.
*/
public ImmutableSet<Scope> getScopes(XConstructorElement injectConstructor) {
checkArgument(injectConstructor.hasAnyAnnotation(TypeNames.INJECT, TypeNames.ASSISTED_INJECT));
checkArgument(hasInjectOrAssistedInjectAnnotation(injectConstructor));
XTypeElement factory = processingEnv.findTypeElement(factoryNameForElement(injectConstructor));
if (factory != null && factory.hasAnnotation(TypeNames.SCOPE_METADATA)) {
String scopeName = factory.getAnnotation(TypeNames.SCOPE_METADATA).getAsString("value");
Expand Down Expand Up @@ -163,7 +166,7 @@ public Optional<AnnotationMirror> getQualifier(Element e) {
}
}
checkNotNull(e);
ImmutableCollection<? extends AnnotationMirror> qualifierAnnotations = getQualifiers(e);
ImmutableList<? extends AnnotationMirror> qualifierAnnotations = getQualifiers(e);
switch (qualifierAnnotations.size()) {
case 0:
return Optional.empty();
Expand All @@ -178,11 +181,10 @@ public Optional<AnnotationMirror> getQualifier(Element e) {
private boolean isFromInjectType(Element element) {
switch (element.getKind()) {
case FIELD:
return isAnnotationPresent(element, TypeNames.INJECT);
return hasInjectAnnotation(element);
case PARAMETER:
// Handles both @Inject constructors and @Inject methods.
return isAnnotationPresent(element.getEnclosingElement(), TypeNames.INJECT)
|| isAnnotationPresent(element.getEnclosingElement(), TypeNames.ASSISTED_INJECT);
return hasInjectOrAssistedInjectAnnotation(element.getEnclosingElement());
default:
return false;
}
Expand All @@ -194,17 +196,19 @@ public ImmutableSet<XAnnotation> getQualifiers(XElement element) {
.collect(toImmutableSet());
}

public ImmutableCollection<? extends AnnotationMirror> getQualifiers(Element element) {
ImmutableSet<? extends AnnotationMirror> qualifiers =
public ImmutableList<? extends AnnotationMirror> getQualifiers(Element element) {
Set<? extends AnnotationMirror> qualifiers =
isFromInjectType(element)
? getQualifiersForInjectType(toXProcessing(element, processingEnv)).stream()
.map(XConverters::toJavac)
.collect(toImmutableSet())
: DaggerElements.getAnnotatedAnnotations(element, TypeNames.QUALIFIER);
: Sets.union(
DaggerElements.getAnnotatedAnnotations(element, TypeNames.QUALIFIER),
DaggerElements.getAnnotatedAnnotations(element, TypeNames.QUALIFIER_JAVAX));
if (element.getKind() == ElementKind.FIELD
// static injected fields are not supported, no need to get qualifier from kotlin metadata
&& !element.getModifiers().contains(STATIC)
&& isAnnotationPresent(element, TypeNames.INJECT)
&& hasInjectAnnotation(element)
&& kotlinMetadataUtil.hasMetadata(element)) {
return Stream.concat(
qualifiers.stream(), getQualifiersForKotlinProperty(asVariable(element)).stream())
Expand All @@ -213,7 +217,7 @@ && isAnnotationPresent(element, TypeNames.INJECT)
.map(Wrapper::get)
.collect(DaggerStreams.toImmutableList());
} else {
return qualifiers.asList();
return ImmutableList.copyOf(qualifiers);
}
}

Expand Down Expand Up @@ -248,15 +252,19 @@ private ImmutableSet<XAnnotation> getQualifiersForInjectType(XElement element) {
// even though we know it's a qualifier because the type is no longer on the classpath. Once
// we fix issue #3136, the superficial validation above will fail in this case, but until then
// keep the same behavior and return an empty set.
return qualifierAnnotation.getType().getTypeElement().hasAnnotation(TypeNames.QUALIFIER)
return qualifierAnnotation.getType().getTypeElement().hasAnyAnnotation(
TypeNames.QUALIFIER, TypeNames.QUALIFIER_JAVAX)
? ImmutableSet.of(qualifierAnnotation)
: ImmutableSet.of();
}

// Fall back to validating all annotations if the ScopeMetadata isn't available.
DaggerSuperficialValidation.strictValidateAnnotationsOf(element);

return ImmutableSet.copyOf(element.getAnnotationsAnnotatedWith(TypeNames.QUALIFIER));
return Sets.union(
element.getAnnotationsAnnotatedWith(TypeNames.QUALIFIER),
element.getAnnotationsAnnotatedWith(TypeNames.QUALIFIER_JAVAX))
.immutableCopy();
}

private ClassName generatedClassNameForInjectType(XElement element) {
Expand All @@ -275,17 +283,39 @@ private ClassName generatedClassNameForInjectType(XElement element) {
/** Returns the constructors in {@code type} that are annotated with {@link Inject}. */
public static ImmutableSet<XConstructorElement> injectedConstructors(XTypeElement type) {
return type.getConstructors().stream()
.filter(constructor -> constructor.hasAnnotation(TypeNames.INJECT))
.filter(InjectionAnnotations::hasInjectAnnotation)
.collect(toImmutableSet());
}

/** Returns the constructors in {@code type} that are annotated with {@link Inject}. */
public static ImmutableSet<ExecutableElement> injectedConstructors(TypeElement type) {
return FluentIterable.from(constructorsIn(type.getEnclosedElements()))
.filter(constructor -> isAnnotationPresent(constructor, TypeNames.INJECT))
.filter(InjectionAnnotations::hasInjectAnnotation)
.toSet();
}

/** Returns true if the given element is annotated with {@link Inject}. */
public static boolean hasInjectAnnotation(XElement element) {
return element.hasAnyAnnotation(TypeNames.INJECT, TypeNames.INJECT_JAVAX);
}

/** Returns true if the given element is annotated with {@link Inject}. */
public static boolean hasInjectAnnotation(Element element) {
return isAnyAnnotationPresent(element, TypeNames.INJECT, TypeNames.INJECT_JAVAX);
}

/** Returns true if the given element is annotated with {@link Inject}. */
public static boolean hasInjectOrAssistedInjectAnnotation(XElement element) {
return element.hasAnyAnnotation(
TypeNames.INJECT, TypeNames.INJECT_JAVAX, TypeNames.ASSISTED_INJECT);
}

/** Returns true if the given element is annotated with {@link Inject}. */
public static boolean hasInjectOrAssistedInjectAnnotation(Element element) {
return isAnyAnnotationPresent(
element, TypeNames.INJECT, TypeNames.INJECT_JAVAX, TypeNames.ASSISTED_INJECT);
}

/**
* Gets the qualifiers annotation of a Kotlin Property. Finding these annotations involve finding
* the synthetic method for annotations as described by the Kotlin metadata or finding the
Expand Down Expand Up @@ -330,7 +360,12 @@ private ImmutableCollection<? extends AnnotationMirror> getQualifiersForKotlinPr
"No MembersInjector found for " + fieldElement.getEnclosingElement());
}
} else {
return kotlinMetadataUtil.getSyntheticPropertyAnnotations(fieldElement, TypeNames.QUALIFIER);
return ImmutableSet.<AnnotationMirror>builder()
.addAll(kotlinMetadataUtil.getSyntheticPropertyAnnotations(
fieldElement, TypeNames.QUALIFIER))
.addAll(kotlinMetadataUtil.getSyntheticPropertyAnnotations(
fieldElement, TypeNames.QUALIFIER_JAVAX))
.build();
}
}
}
Expand Up @@ -20,7 +20,6 @@
import static com.google.auto.common.MoreTypes.asDeclared;
import static com.google.common.base.Preconditions.checkArgument;
import static dagger.internal.codegen.langmodel.DaggerElements.DECLARATION_ORDER;
import static dagger.internal.codegen.langmodel.DaggerElements.isAnnotationPresent;
import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.STATIC;
Expand All @@ -32,7 +31,6 @@
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.SetMultimap;
import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.langmodel.DaggerElements;
import dagger.internal.codegen.langmodel.DaggerTypes;
import java.util.ArrayList;
Expand Down Expand Up @@ -150,7 +148,7 @@ public Optional<InjectionSite> visitVariableAsField(
}

private boolean shouldBeInjected(Element injectionSite) {
return isAnnotationPresent(injectionSite, TypeNames.INJECT)
return InjectionAnnotations.hasInjectAnnotation(injectionSite)
&& !injectionSite.getModifiers().contains(PRIVATE)
&& !injectionSite.getModifiers().contains(STATIC);
}
Expand Down
Expand Up @@ -17,7 +17,6 @@
package dagger.internal.codegen.binding;

import static androidx.room.compiler.processing.compat.XConverters.toJavac;
import static dagger.internal.codegen.langmodel.DaggerElements.isAnnotationPresent;
import static java.util.stream.Collectors.toList;

import androidx.room.compiler.processing.XElement;
Expand All @@ -26,7 +25,6 @@
import com.google.auto.value.extension.memoized.Memoized;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.spi.model.BindingKind;
import dagger.spi.model.DependencyRequest;
import dagger.spi.model.Key;
Expand Down Expand Up @@ -134,7 +132,7 @@ public int indexAmongAtInjectMembersWithSameSimpleName() {
.getEnclosingElement()
.getEnclosedElements()
.stream()
.filter(element -> isAnnotationPresent(element, TypeNames.INJECT))
.filter(InjectionAnnotations::hasInjectAnnotation)
.filter(element -> !element.getModifiers().contains(Modifier.PRIVATE))
.filter(element -> element.getSimpleName().equals(this.element().getSimpleName()))
.collect(toList())
Expand Down
12 changes: 8 additions & 4 deletions java/dagger/internal/codegen/javapoet/TypeNames.java
Expand Up @@ -140,10 +140,14 @@ public final class TypeNames {
public static final ClassName MAP = ClassName.get("java.util", "Map");
public static final ClassName IMMUTABLE_MAP =
ClassName.get("com.google.common.collect", "ImmutableMap");
public static final ClassName SINGLETON = ClassName.get("javax.inject", "Singleton");
public static final ClassName SCOPE = ClassName.get("javax.inject", "Scope");
public static final ClassName INJECT = ClassName.get("javax.inject", "Inject");
public static final ClassName QUALIFIER = ClassName.get("javax.inject", "Qualifier");
public static final ClassName SINGLETON = ClassName.get("jakarta.inject", "Singleton");
public static final ClassName SINGLETON_JAVAX = ClassName.get("javax.inject", "Singleton");
public static final ClassName SCOPE = ClassName.get("jakarta.inject", "Scope");
public static final ClassName SCOPE_JAVAX = ClassName.get("javax.inject", "Scope");
public static final ClassName INJECT = ClassName.get("jakarta.inject", "Inject");
public static final ClassName INJECT_JAVAX = ClassName.get("javax.inject", "Inject");
public static final ClassName QUALIFIER = ClassName.get("jakarta.inject", "Qualifier");
public static final ClassName QUALIFIER_JAVAX = ClassName.get("javax.inject", "Qualifier");
public static final ClassName COLLECTION = ClassName.get("java.util", "Collection");
public static final ClassName LIST = ClassName.get("java.util", "List");
public static final ClassName SET = ClassName.get("java.util", "Set");
Expand Down
Expand Up @@ -202,7 +202,8 @@ private void validateDependencyScopes(ComponentDescriptor component) {
if (!scopes.isEmpty()) {
// Dagger 1.x scope compatibility requires this be suppress-able.
if (compilerOptions.scopeCycleValidationType().diagnosticKind().isPresent()
&& scopes.contains(TypeNames.SINGLETON)) {
&& (scopes.contains(TypeNames.SINGLETON)
|| scopes.contains(TypeNames.SINGLETON_JAVAX))) {
// Singleton is a special-case representing the longest lifetime, and therefore
// @Singleton components may not depend on scoped components
if (!scopedDependencies.isEmpty()) {
Expand Down

0 comments on commit 2195513

Please sign in to comment.