From 8b30cb523e3d2aad9841ad528eda80f445f9336b Mon Sep 17 00:00:00 2001 From: Eric Chang Date: Tue, 28 Dec 2021 14:23:53 -0800 Subject: [PATCH] Make Dagger recognize Jakarta versions of Inject, Scope, Qualifier, and 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: 418678754 --- WORKSPACE | 1 + .../processor/AndroidInjectorDescriptor.java | 11 +-- .../processor/AndroidMapKeyValidator.java | 7 +- java/dagger/android/processor/TypeNames.java | 6 +- .../codegen/InjectProcessingStep.java | 2 +- java/dagger/internal/codegen/base/Scopes.java | 4 +- .../codegen/binding/BindingFactory.java | 4 +- .../codegen/binding/InjectionAnnotations.java | 67 ++++++++++++++----- .../codegen/binding/InjectionSiteFactory.java | 4 +- .../binding/MembersInjectionBinding.java | 4 +- .../internal/codegen/javapoet/TypeNames.java | 12 ++-- .../ComponentDescriptorValidator.java | 3 +- .../codegen/validation/InjectValidator.java | 17 +++-- .../codegen/validation/ModuleValidator.java | 7 +- javatests/dagger/functional/jakarta/BUILD | 34 ++++++++++ .../functional/jakarta/SimpleJakartaTest.java | 67 +++++++++++++++++++ 16 files changed, 200 insertions(+), 50 deletions(-) create mode 100644 javatests/dagger/functional/jakarta/BUILD create mode 100644 javatests/dagger/functional/jakarta/SimpleJakartaTest.java diff --git a/WORKSPACE b/WORKSPACE index 43f6a91d052..eaa33e841bc 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -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", diff --git a/java/dagger/android/processor/AndroidInjectorDescriptor.java b/java/dagger/android/processor/AndroidInjectorDescriptor.java index 7be8798107e..0b9d74b6db8 100644 --- a/java/dagger/android/processor/AndroidInjectorDescriptor.java +++ b/java/dagger/android/processor/AndroidInjectorDescriptor.java @@ -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; @@ -126,13 +127,15 @@ Optional 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); } diff --git a/java/dagger/android/processor/AndroidMapKeyValidator.java b/java/dagger/android/processor/AndroidMapKeyValidator.java index acb704c1a5e..02135265ef4 100644 --- a/java/dagger/android/processor/AndroidMapKeyValidator.java +++ b/java/dagger/android/processor/AndroidMapKeyValidator.java @@ -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; @@ -78,7 +79,8 @@ public ImmutableSet process(ImmutableSetMultimap 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; } @@ -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()) diff --git a/java/dagger/android/processor/TypeNames.java b/java/dagger/android/processor/TypeNames.java index 3b42b459dc0..7325690cae9 100644 --- a/java/dagger/android/processor/TypeNames.java +++ b/java/dagger/android/processor/TypeNames.java @@ -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() {} } diff --git a/java/dagger/internal/codegen/InjectProcessingStep.java b/java/dagger/internal/codegen/InjectProcessingStep.java index bc677c5aa87..a201afec0a7 100644 --- a/java/dagger/internal/codegen/InjectProcessingStep.java +++ b/java/dagger/internal/codegen/InjectProcessingStep.java @@ -51,7 +51,7 @@ final class InjectProcessingStep extends TypeCheckingProcessingStep { @Override public ImmutableSet annotationClassNames() { - return ImmutableSet.of(TypeNames.INJECT, TypeNames.ASSISTED_INJECT); + return ImmutableSet.of(TypeNames.INJECT, TypeNames.INJECT_JAVAX, TypeNames.ASSISTED_INJECT); } @Override diff --git a/java/dagger/internal/codegen/base/Scopes.java b/java/dagger/internal/codegen/base/Scopes.java index 56d922c5942..2c876c1b4cd 100644 --- a/java/dagger/internal/codegen/base/Scopes.java +++ b/java/dagger/internal/codegen/base/Scopes.java @@ -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; @@ -57,7 +58,8 @@ public static String getReadableSource(Scope scope) { /** Returns all of the associated scopes for a source code element. */ public static ImmutableSet 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()); diff --git a/java/dagger/internal/codegen/binding/BindingFactory.java b/java/dagger/internal/codegen/binding/BindingFactory.java index bbd4e9064a5..9c541300e8a 100644 --- a/java/dagger/internal/codegen/binding/BindingFactory.java +++ b/java/dagger/internal/codegen/binding/BindingFactory.java @@ -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 resolvedEnclosingType) { - checkArgument( - constructorElement.hasAnnotation(TypeNames.INJECT) - || constructorElement.hasAnnotation(TypeNames.ASSISTED_INJECT)); + checkArgument(InjectionAnnotations.hasInjectOrAssistedInjectAnnotation(constructorElement)); XConstructorType constructorType = constructorElement.getExecutableType(); XType enclosingType = constructorElement.getEnclosingElement().getType(); diff --git a/java/dagger/internal/codegen/binding/InjectionAnnotations.java b/java/dagger/internal/codegen/binding/InjectionAnnotations.java index 46a6eb1d7b9..01cc5454d53 100644 --- a/java/dagger/internal/codegen/binding/InjectionAnnotations.java +++ b/java/dagger/internal/codegen/binding/InjectionAnnotations.java @@ -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; @@ -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; @@ -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; @@ -109,7 +112,7 @@ public Optional getScope(XConstructorElement injectConstructor) { * @throws IllegalArgumentException if the given constructor is not an inject constructor. */ public ImmutableSet 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"); @@ -163,7 +166,7 @@ public Optional getQualifier(Element e) { } } checkNotNull(e); - ImmutableCollection qualifierAnnotations = getQualifiers(e); + ImmutableList qualifierAnnotations = getQualifiers(e); switch (qualifierAnnotations.size()) { case 0: return Optional.empty(); @@ -178,11 +181,10 @@ public Optional 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; } @@ -194,17 +196,19 @@ public ImmutableSet getQualifiers(XElement element) { .collect(toImmutableSet()); } - public ImmutableCollection getQualifiers(Element element) { - ImmutableSet qualifiers = + public ImmutableList getQualifiers(Element element) { + Set 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()) @@ -213,7 +217,7 @@ && isAnnotationPresent(element, TypeNames.INJECT) .map(Wrapper::get) .collect(DaggerStreams.toImmutableList()); } else { - return qualifiers.asList(); + return ImmutableList.copyOf(qualifiers); } } @@ -248,7 +252,8 @@ private ImmutableSet 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(); } @@ -256,7 +261,10 @@ private ImmutableSet getQualifiersForInjectType(XElement element) { // 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) { @@ -275,17 +283,39 @@ private ClassName generatedClassNameForInjectType(XElement element) { /** Returns the constructors in {@code type} that are annotated with {@link Inject}. */ public static ImmutableSet 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 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 @@ -330,7 +360,12 @@ private ImmutableCollection getQualifiersForKotlinPr "No MembersInjector found for " + fieldElement.getEnclosingElement()); } } else { - return kotlinMetadataUtil.getSyntheticPropertyAnnotations(fieldElement, TypeNames.QUALIFIER); + return ImmutableSet.builder() + .addAll(kotlinMetadataUtil.getSyntheticPropertyAnnotations( + fieldElement, TypeNames.QUALIFIER)) + .addAll(kotlinMetadataUtil.getSyntheticPropertyAnnotations( + fieldElement, TypeNames.QUALIFIER_JAVAX)) + .build(); } } } diff --git a/java/dagger/internal/codegen/binding/InjectionSiteFactory.java b/java/dagger/internal/codegen/binding/InjectionSiteFactory.java index f039784b3fc..e620c058aa5 100644 --- a/java/dagger/internal/codegen/binding/InjectionSiteFactory.java +++ b/java/dagger/internal/codegen/binding/InjectionSiteFactory.java @@ -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; @@ -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; @@ -150,7 +148,7 @@ public Optional visitVariableAsField( } private boolean shouldBeInjected(Element injectionSite) { - return isAnnotationPresent(injectionSite, TypeNames.INJECT) + return InjectionAnnotations.hasInjectAnnotation(injectionSite) && !injectionSite.getModifiers().contains(PRIVATE) && !injectionSite.getModifiers().contains(STATIC); } diff --git a/java/dagger/internal/codegen/binding/MembersInjectionBinding.java b/java/dagger/internal/codegen/binding/MembersInjectionBinding.java index 5070cba273c..695aea6a7e3 100644 --- a/java/dagger/internal/codegen/binding/MembersInjectionBinding.java +++ b/java/dagger/internal/codegen/binding/MembersInjectionBinding.java @@ -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; @@ -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; @@ -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()) diff --git a/java/dagger/internal/codegen/javapoet/TypeNames.java b/java/dagger/internal/codegen/javapoet/TypeNames.java index 701e18f3806..ae4145d666e 100644 --- a/java/dagger/internal/codegen/javapoet/TypeNames.java +++ b/java/dagger/internal/codegen/javapoet/TypeNames.java @@ -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"); diff --git a/java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java b/java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java index aba9bbc9c85..fe2f3a294a6 100644 --- a/java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java +++ b/java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java @@ -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()) { diff --git a/java/dagger/internal/codegen/validation/InjectValidator.java b/java/dagger/internal/codegen/validation/InjectValidator.java index 3330d6e296a..2faa2bd288f 100644 --- a/java/dagger/internal/codegen/validation/InjectValidator.java +++ b/java/dagger/internal/codegen/validation/InjectValidator.java @@ -177,15 +177,17 @@ private ValidationReport validateConstructor(XConstructorElement constructorElem ValidationReport.Builder builder = ValidationReport.about(constructorElement.getEnclosingElement()); - if (constructorElement.hasAnnotation(TypeNames.INJECT) + if (InjectionAnnotations.hasInjectAnnotation(constructorElement) && constructorElement.hasAnnotation(TypeNames.ASSISTED_INJECT)) { builder.addError("Constructors cannot be annotated with both @Inject and @AssistedInject"); } ClassName injectAnnotation = - getAnyAnnotation(constructorElement, TypeNames.INJECT, TypeNames.ASSISTED_INJECT) - .map(XAnnotations::getClassName) - .get(); + getAnyAnnotation( + constructorElement, + TypeNames.INJECT, + TypeNames.INJECT_JAVAX, + TypeNames.ASSISTED_INJECT).map(XAnnotations::getClassName).get(); if (constructorElement.isPrivate()) { builder.addError( @@ -212,7 +214,8 @@ private ValidationReport validateConstructor(XConstructorElement constructorElem "@Scope annotations are not allowed on @%s constructors", injectAnnotation.simpleName()); - if (injectAnnotation.equals(TypeNames.INJECT)) { + if (injectAnnotation.equals(TypeNames.INJECT) + || injectAnnotation.equals(TypeNames.INJECT_JAVAX)) { scopeErrorMsg += "; annotate the class instead"; } @@ -369,7 +372,7 @@ private ValidationReport validateForMembersInjectionInternalUncached(XTypeElemen ValidationReport.Builder builder = ValidationReport.about(typeElement); boolean hasInjectedMembers = false; for (XFieldElement field : typeElement.getDeclaredFields()) { - if (field.hasAnnotation(TypeNames.INJECT)) { + if (InjectionAnnotations.hasInjectAnnotation(field)) { hasInjectedMembers = true; ValidationReport report = validateField(field); if (!report.isClean()) { @@ -378,7 +381,7 @@ private ValidationReport validateForMembersInjectionInternalUncached(XTypeElemen } } for (XMethodElement method : typeElement.getDeclaredMethods()) { - if (method.hasAnnotation(TypeNames.INJECT)) { + if (InjectionAnnotations.hasInjectAnnotation(method)) { hasInjectedMembers = true; ValidationReport report = validateMethod(method); if (!report.isClean()) { diff --git a/java/dagger/internal/codegen/validation/ModuleValidator.java b/java/dagger/internal/codegen/validation/ModuleValidator.java index 90ee8ae5877..24573ff3b41 100644 --- a/java/dagger/internal/codegen/validation/ModuleValidator.java +++ b/java/dagger/internal/codegen/validation/ModuleValidator.java @@ -26,7 +26,6 @@ import static dagger.internal.codegen.extension.DaggerCollectors.toOptional; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; import static dagger.internal.codegen.xprocessing.XAnnotations.getClassName; -import static dagger.internal.codegen.xprocessing.XElements.getAnnotatedAnnotations; import static dagger.internal.codegen.xprocessing.XElements.getSimpleName; import static dagger.internal.codegen.xprocessing.XElements.hasAnyAnnotation; import static dagger.internal.codegen.xprocessing.XTypeElements.hasTypeParameters; @@ -53,6 +52,7 @@ import com.google.common.collect.Sets; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.TypeName; +import dagger.internal.codegen.base.Scopes; import dagger.internal.codegen.binding.BindingGraphFactory; import dagger.internal.codegen.binding.ComponentCreatorAnnotation; import dagger.internal.codegen.binding.ComponentDescriptorFactory; @@ -61,6 +61,7 @@ import dagger.internal.codegen.javapoet.TypeNames; import dagger.internal.codegen.xprocessing.XElements; import dagger.spi.model.BindingGraph; +import dagger.spi.model.Scope; import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; @@ -550,13 +551,13 @@ private boolean requiresModuleInstance(XTypeElement module) { private void validateNoScopeAnnotationsOnModuleElement( XTypeElement module, ModuleKind moduleKind, ValidationReport.Builder report) { - for (XAnnotation scope : getAnnotatedAnnotations(module, TypeNames.SCOPE)) { + for (Scope scope : Scopes.scopesOf(module)) { report.addError( String.format( "@%ss cannot be scoped. Did you mean to scope a method instead?", moduleKind.annotation().simpleName()), module, - scope); + scope.scopeAnnotation().xprocessing()); } } diff --git a/javatests/dagger/functional/jakarta/BUILD b/javatests/dagger/functional/jakarta/BUILD new file mode 100644 index 00000000000..7fc1ec23e29 --- /dev/null +++ b/javatests/dagger/functional/jakarta/BUILD @@ -0,0 +1,34 @@ +# Copyright (C) 2022 The Dagger Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Description: +# Functional tests for Dagger that depend on jakarta.inject. + +load("//:build_defs.bzl", "DOCLINT_HTML_AND_SYNTAX") +load("//:test_defs.bzl", "GenJavaTests") + +package(default_visibility = ["//:src"]) + +# TODO(b/203233586): Replace with GenJavaTest +GenJavaTests( + name = "SimpleJakartaTest", + srcs = ["SimpleJakartaTest.java"], + javacopts = DOCLINT_HTML_AND_SYNTAX, + deps = [ + "//:dagger_with_compiler", + "@maven//:jakarta_inject_jakarta_inject_api", + "//third_party/java/junit", + "//third_party/java/truth", + ], +) diff --git a/javatests/dagger/functional/jakarta/SimpleJakartaTest.java b/javatests/dagger/functional/jakarta/SimpleJakartaTest.java new file mode 100644 index 00000000000..e943eb0b892 --- /dev/null +++ b/javatests/dagger/functional/jakarta/SimpleJakartaTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2020 The Dagger Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dagger.functional.jakarta; + +import static com.google.common.truth.Truth.assertThat; + +import dagger.Binds; +import dagger.Component; +import dagger.Module; +import jakarta.inject.Inject; +import jakarta.inject.Qualifier; +import jakarta.inject.Scope; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class SimpleJakartaTest { + + @Scope + public @interface TestScope {} + + @Qualifier + public @interface TestQualifier {} + + @TestScope + @Component(modules = TestModule.class) + interface TestComponent { + @TestQualifier Foo getQualifiedFoo(); + } + + public static final class Foo { + @Inject Foo() {} + } + + @Module + interface TestModule { + // By binding this to itself, if the qualifier annotation isn't picked up, it will created a + // cycle. + @Binds + @TestScope + @TestQualifier + Foo bind(Foo impl); + } + + @Test + public void testFooFactory() { + TestComponent testComponent = DaggerSimpleJakartaTest_TestComponent.create(); + Foo foo = testComponent.getQualifiedFoo(); + + assertThat(foo).isSameInstanceAs(testComponent.getQualifiedFoo()); + } +}