aboutsummaryrefslogtreecommitdiff
path: root/java/dagger/android/processor
diff options
context:
space:
mode:
Diffstat (limited to 'java/dagger/android/processor')
-rw-r--r--java/dagger/android/processor/AndroidInjectorDescriptor.java116
-rw-r--r--java/dagger/android/processor/AndroidMapKeyProcessingStep.java163
-rw-r--r--java/dagger/android/processor/AndroidMapKeyValidator.java175
-rw-r--r--java/dagger/android/processor/AndroidMapKeys.java21
-rw-r--r--java/dagger/android/processor/AndroidProcessor.java59
-rw-r--r--java/dagger/android/processor/BUILD60
-rw-r--r--java/dagger/android/processor/BaseProcessingStep.java93
-rw-r--r--java/dagger/android/processor/ContributesAndroidInjectorProcessingStep.java (renamed from java/dagger/android/processor/ContributesAndroidInjectorGenerator.java)114
-rw-r--r--java/dagger/android/processor/DelegateAndroidProcessor.java55
-rw-r--r--java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java59
-rw-r--r--java/dagger/android/processor/KspAndroidProcessor.java53
-rw-r--r--java/dagger/android/processor/MoreDaggerElements.java71
-rw-r--r--java/dagger/android/processor/MoreDaggerTypes.java114
13 files changed, 575 insertions, 578 deletions
diff --git a/java/dagger/android/processor/AndroidInjectorDescriptor.java b/java/dagger/android/processor/AndroidInjectorDescriptor.java
index 12f2edf24..758d895aa 100644
--- a/java/dagger/android/processor/AndroidInjectorDescriptor.java
+++ b/java/dagger/android/processor/AndroidInjectorDescriptor.java
@@ -16,29 +16,23 @@
package dagger.android.processor;
-import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
-import static dagger.android.processor.MoreDaggerElements.getAnnotatedAnnotations;
-import static java.util.stream.Collectors.toList;
-import static javax.lang.model.element.Modifier.ABSTRACT;
-
-import com.google.auto.common.MoreElements;
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.JavaPoetExtKt;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XAnnotationValue;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XExecutableElement;
+import androidx.room.compiler.processing.XMessager;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
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;
-import java.util.List;
+import dagger.internal.codegen.xprocessing.XElements;
import java.util.Optional;
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleAnnotationValueVisitor8;
import javax.tools.Diagnostic.Kind;
/**
@@ -60,7 +54,7 @@ abstract class AndroidInjectorDescriptor {
abstract ClassName enclosingModule();
/** The method annotated with {@code ContributesAndroidInjector}. */
- abstract ExecutableElement method();
+ abstract XExecutableElement method();
@AutoValue.Builder
abstract static class Builder {
@@ -72,15 +66,15 @@ abstract class AndroidInjectorDescriptor {
abstract Builder enclosingModule(ClassName enclosingModule);
- abstract Builder method(ExecutableElement method);
+ abstract Builder method(XExecutableElement method);
abstract AndroidInjectorDescriptor build();
}
static final class Validator {
- private final Messager messager;
+ private final XMessager messager;
- Validator(Messager messager) {
+ Validator(XMessager messager) {
this.messager = messager;
}
@@ -88,10 +82,10 @@ abstract class AndroidInjectorDescriptor {
* Validates a {@code ContributesAndroidInjector} method, returning an {@link
* AndroidInjectorDescriptor} if it is valid, or {@link Optional#empty()} otherwise.
*/
- Optional<AndroidInjectorDescriptor> createIfValid(ExecutableElement method) {
+ Optional<AndroidInjectorDescriptor> createIfValid(XMethodElement method) {
ErrorReporter reporter = new ErrorReporter(method, messager);
- if (!method.getModifiers().contains(ABSTRACT)) {
+ if (!method.isAbstract()) {
reporter.reportError("@ContributesAndroidInjector methods must be abstract");
}
@@ -101,41 +95,40 @@ abstract class AndroidInjectorDescriptor {
AndroidInjectorDescriptor.Builder builder =
new AutoValue_AndroidInjectorDescriptor.Builder().method(method);
- TypeElement enclosingElement = MoreElements.asType(method.getEnclosingElement());
- if (!MoreDaggerElements.isAnnotationPresent(enclosingElement, TypeNames.MODULE)) {
+ XTypeElement enclosingElement = XElements.asTypeElement(method.getEnclosingElement());
+ if (!enclosingElement.hasAnnotation(TypeNames.MODULE)) {
reporter.reportError("@ContributesAndroidInjector methods must be in a @Module");
}
- builder.enclosingModule(ClassName.get(enclosingElement));
+ builder.enclosingModule(enclosingElement.getClassName());
- TypeMirror injectedType = method.getReturnType();
- if (MoreTypes.asDeclared(injectedType).getTypeArguments().isEmpty()) {
- builder.injectedType(ClassName.get(MoreTypes.asTypeElement(injectedType)));
+ XType injectedType = method.getReturnType();
+ if (injectedType.getTypeArguments().isEmpty()) {
+ builder.injectedType(injectedType.getTypeElement().getClassName());
} else {
reporter.reportError(
"@ContributesAndroidInjector methods cannot return parameterized types");
}
- AnnotationMirror annotation =
- MoreDaggerElements.getAnnotationMirror(method, TypeNames.CONTRIBUTES_ANDROID_INJECTOR)
- .get();
- for (TypeMirror module :
- getAnnotationValue(annotation, "modules").accept(new AllTypesVisitor(), null)) {
- if (MoreDaggerElements.isAnnotationPresent(MoreTypes.asElement(module), TypeNames.MODULE)) {
- builder.modulesBuilder().add((ClassName) TypeName.get(module));
+ XAnnotation annotation = method.getAnnotation(TypeNames.CONTRIBUTES_ANDROID_INJECTOR);
+ for (XType module : getTypeList(annotation.getAnnotationValue("modules"))) {
+ if (module.getTypeElement().hasAnnotation(TypeNames.MODULE)) {
+ builder.modulesBuilder().add((ClassName) module.getTypeName());
} else {
reporter.reportError(String.format("%s is not a @Module", module), annotation);
}
}
- for (AnnotationMirror scope : Sets.union(
- getAnnotatedAnnotations(method, TypeNames.SCOPE),
- getAnnotatedAnnotations(method, TypeNames.SCOPE_JAVAX))) {
- builder.scopesBuilder().add(AnnotationSpec.get(scope));
+ for (XAnnotation scope :
+ Sets.union(
+ method.getAnnotationsAnnotatedWith(TypeNames.SCOPE),
+ method.getAnnotationsAnnotatedWith(TypeNames.SCOPE_JAVAX))) {
+ builder.scopesBuilder().add(JavaPoetExtKt.toAnnotationSpec(scope));
}
- for (AnnotationMirror qualifier : Sets.union(
- getAnnotatedAnnotations(method, TypeNames.QUALIFIER),
- getAnnotatedAnnotations(method, TypeNames.QUALIFIER_JAVAX))) {
+ for (XAnnotation qualifier :
+ Sets.union(
+ method.getAnnotationsAnnotatedWith(TypeNames.QUALIFIER),
+ method.getAnnotationsAnnotatedWith(TypeNames.QUALIFIER_JAVAX))) {
reporter.reportError(
"@ContributesAndroidInjector methods cannot have qualifiers", qualifier);
}
@@ -143,13 +136,23 @@ abstract class AndroidInjectorDescriptor {
return reporter.hasError ? Optional.empty() : Optional.of(builder.build());
}
+ private static ImmutableList<XType> getTypeList(XAnnotationValue annotationValue) {
+ if (annotationValue.hasTypeListValue()) {
+ return ImmutableList.copyOf(annotationValue.asTypeList());
+ }
+ if (annotationValue.hasTypeValue()) {
+ return ImmutableList.of(annotationValue.asType());
+ }
+ throw new IllegalArgumentException("Does not have type list");
+ }
+
// TODO(ronshapiro): use ValidationReport once it is moved out of the compiler
private static class ErrorReporter {
- private final Element subject;
- private final Messager messager;
+ private final XElement subject;
+ private final XMessager messager;
private boolean hasError;
- ErrorReporter(Element subject, Messager messager) {
+ ErrorReporter(XElement subject, XMessager messager) {
this.subject = subject;
this.messager = messager;
}
@@ -159,29 +162,10 @@ abstract class AndroidInjectorDescriptor {
messager.printMessage(Kind.ERROR, error, subject);
}
- void reportError(String error, AnnotationMirror annotation) {
+ void reportError(String error, XAnnotation annotation) {
hasError = true;
messager.printMessage(Kind.ERROR, error, subject, annotation);
}
}
}
-
- private static final class AllTypesVisitor
- extends SimpleAnnotationValueVisitor8<ImmutableSet<TypeMirror>, Void> {
- @Override
- public ImmutableSet<TypeMirror> visitArray(List<? extends AnnotationValue> values, Void aVoid) {
- return ImmutableSet.copyOf(
- values.stream().flatMap(v -> v.accept(this, null).stream()).collect(toList()));
- }
-
- @Override
- public ImmutableSet<TypeMirror> visitType(TypeMirror a, Void aVoid) {
- return ImmutableSet.of(a);
- }
-
- @Override
- protected ImmutableSet<TypeMirror> defaultAction(Object o, Void aVoid) {
- throw new AssertionError(o);
- }
- }
}
diff --git a/java/dagger/android/processor/AndroidMapKeyProcessingStep.java b/java/dagger/android/processor/AndroidMapKeyProcessingStep.java
new file mode 100644
index 000000000..8a7e83c77
--- /dev/null
+++ b/java/dagger/android/processor/AndroidMapKeyProcessingStep.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2017 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.android.processor;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.android.processor.AndroidMapKeys.injectedTypeFromMapKey;
+import static dagger.internal.codegen.xprocessing.XTypes.toStableString;
+
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XMethodElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XType;
+import androidx.room.compiler.processing.XTypeElement;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.squareup.javapoet.ClassName;
+import dagger.internal.codegen.xprocessing.XElements;
+import dagger.internal.codegen.xprocessing.XTypes;
+import javax.tools.Diagnostic.Kind;
+
+/** Validates the correctness of {@link dagger.MapKey}s used with {@code dagger.android}. */
+final class AndroidMapKeyProcessingStep extends BaseProcessingStep {
+ private final XProcessingEnv processingEnv;
+
+ AndroidMapKeyProcessingStep(XProcessingEnv processingEnv) {
+ this.processingEnv = processingEnv;
+ }
+
+ @Override
+ public ImmutableSet<ClassName> annotationClassNames() {
+ return ImmutableSet.of(TypeNames.ANDROID_INJECTION_KEY, TypeNames.CLASS_KEY);
+ }
+
+ @Override
+ public void process(XElement element, ImmutableSet<ClassName> annotationNames) {
+ for (ClassName annotationName : annotationNames) {
+ validateMethod(annotationName, XElements.asMethod(element));
+ }
+ }
+
+ private void validateMethod(ClassName annotation, XMethodElement method) {
+ if (!Sets.union(
+ method.getAnnotationsAnnotatedWith(TypeNames.QUALIFIER),
+ method.getAnnotationsAnnotatedWith(TypeNames.QUALIFIER_JAVAX))
+ .isEmpty()) {
+ return;
+ }
+
+ XType returnType = method.getReturnType();
+ if (!factoryElement().getType().getRawType().isAssignableFrom(returnType.getRawType())) {
+ // if returnType is not related to AndroidInjector.Factory, ignore the method
+ return;
+ }
+
+ if (!Sets.union(
+ method.getAnnotationsAnnotatedWith(TypeNames.SCOPE),
+ method.getAnnotationsAnnotatedWith(TypeNames.SCOPE_JAVAX))
+ .isEmpty()) {
+ XAnnotation suppressedWarnings = method.getAnnotation(ClassName.get(SuppressWarnings.class));
+ if (suppressedWarnings == null
+ || !ImmutableSet.copyOf(suppressedWarnings.getAsStringList("value"))
+ .contains("dagger.android.ScopedInjectorFactory")) {
+ XAnnotation mapKeyAnnotation =
+ getOnlyElement(method.getAnnotationsAnnotatedWith(TypeNames.MAP_KEY));
+ XTypeElement mapKeyValueElement =
+ processingEnv.requireTypeElement(injectedTypeFromMapKey(mapKeyAnnotation).get());
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Kind.ERROR,
+ String.format(
+ "%s bindings should not be scoped. Scoping this method may leak instances of"
+ + " %s.",
+ TypeNames.ANDROID_INJECTOR_FACTORY.canonicalName(),
+ mapKeyValueElement.getQualifiedName()),
+ method);
+ }
+ }
+
+ validateReturnType(method);
+
+ // @Binds methods should only have one parameter, but we can't guarantee the order of Processors
+ // in javac, so do a basic check for valid form
+ if (method.hasAnnotation(TypeNames.BINDS) && method.getParameters().size() == 1) {
+ validateMapKeyMatchesBindsParameter(annotation, method);
+ }
+ }
+
+ /** Report an error if the method's return type is not {@code AndroidInjector.Factory<?>}. */
+ private void validateReturnType(XMethodElement method) {
+ XType returnType = method.getReturnType();
+ XType requiredReturnType = injectorFactoryOf(processingEnv.getWildcardType(null, null));
+
+ // TODO(b/311460276) use XType.isSameType when the bug is fixed.
+ if (!returnType.getTypeName().equals(requiredReturnType.getTypeName())) {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Kind.ERROR,
+ String.format(
+ "%s should bind %s, not %s. See https://dagger.dev/android",
+ method, toStableString(requiredReturnType), toStableString(returnType)),
+ method);
+ }
+ }
+
+ /**
+ * A valid @Binds method could bind an {@code AndroidInjector.Factory} for one type, while giving
+ * it a map key of a different type. The return type and parameter type would pass typical @Binds
+ * validation, but the map lookup in {@code DispatchingAndroidInjector} would retrieve the wrong
+ * injector factory.
+ *
+ * <pre>{@code
+ * {@literal @Binds}
+ * {@literal @IntoMap}
+ * {@literal @ClassKey(GreenActivity.class)}
+ * abstract AndroidInjector.Factory<?> bindBlueActivity(
+ * BlueActivityComponent.Builder builder);
+ * }</pre>
+ */
+ private void validateMapKeyMatchesBindsParameter(
+ ClassName annotationName, XMethodElement method) {
+ XType parameterType = getOnlyElement(method.getParameters()).getType();
+ XAnnotation annotation = method.getAnnotation(annotationName);
+ XType mapKeyType =
+ processingEnv.requireTypeElement(injectedTypeFromMapKey(annotation).get()).getType();
+ if (!XTypes.isAssignableTo(parameterType, injectorFactoryOf(mapKeyType))) {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ Kind.ERROR,
+ String.format(
+ "%s does not implement AndroidInjector<%s>",
+ toStableString(parameterType), toStableString(mapKeyType)),
+ method,
+ annotation);
+ }
+ }
+
+ /** Returns a {@link XType} for {@code AndroidInjector.Factory<implementationType>}. */
+ private XType injectorFactoryOf(XType implementationType) {
+ return processingEnv.getDeclaredType(factoryElement(), implementationType);
+ }
+
+ private XTypeElement factoryElement() {
+ return processingEnv.requireTypeElement(TypeNames.ANDROID_INJECTOR_FACTORY.canonicalName());
+ }
+}
diff --git a/java/dagger/android/processor/AndroidMapKeyValidator.java b/java/dagger/android/processor/AndroidMapKeyValidator.java
deleted file mode 100644
index 8d674666b..000000000
--- a/java/dagger/android/processor/AndroidMapKeyValidator.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2017 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.android.processor;
-
-import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.android.processor.AndroidMapKeys.injectedTypeFromMapKey;
-import static dagger.android.processor.MoreDaggerElements.getAnnotatedAnnotations;
-
-import com.google.auto.common.BasicAnnotationProcessor.Step;
-import com.google.auto.common.MoreElements;
-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;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
-import javax.tools.Diagnostic.Kind;
-
-/** Validates the correctness of {@link dagger.MapKey}s used with {@code dagger.android}. */
-final class AndroidMapKeyValidator implements Step {
- private static final ImmutableMap<String, ClassName> SUPPORTED_ANNOTATIONS =
- ImmutableMap.of(
- TypeNames.ANDROID_INJECTION_KEY.toString(), TypeNames.ANDROID_INJECTION_KEY,
- TypeNames.CLASS_KEY.toString(), TypeNames.CLASS_KEY);
-
- private final Elements elements;
- private final Types types;
- private final Messager messager;
-
- AndroidMapKeyValidator(Elements elements, Types types, Messager messager) {
- this.elements = elements;
- this.types = types;
- this.messager = messager;
- }
-
- @Override
- public ImmutableSet<String> annotations() {
- return SUPPORTED_ANNOTATIONS.keySet();
- }
-
- @Override
- public ImmutableSet<Element> process(ImmutableSetMultimap<String, Element> elementsByAnnotation) {
- ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
- elementsByAnnotation
- .entries()
- .forEach(
- entry -> {
- try {
- validateMethod(entry.getKey(), MoreElements.asExecutable(entry.getValue()));
- } catch (TypeNotPresentException e) {
- deferredElements.add(entry.getValue());
- }
- });
- return deferredElements.build();
- }
-
- private void validateMethod(String annotation, ExecutableElement method) {
- if (!Sets.union(getAnnotatedAnnotations(method, TypeNames.QUALIFIER),
- getAnnotatedAnnotations(method, TypeNames.QUALIFIER_JAVAX)).isEmpty()) {
- return;
- }
-
- TypeMirror returnType = method.getReturnType();
- if (!types.isAssignable(types.erasure(returnType), factoryElement().asType())) {
- // if returnType is not related to AndroidInjector.Factory, ignore the method
- return;
- }
-
- 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())
- .contains("dagger.android.ScopedInjectorFactory")) {
- AnnotationMirror mapKeyAnnotation =
- getOnlyElement(getAnnotatedAnnotations(method, MapKey.class));
- TypeElement mapKeyValueElement =
- elements.getTypeElement(injectedTypeFromMapKey(mapKeyAnnotation).get());
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "%s bindings should not be scoped. Scoping this method may leak instances of %s.",
- TypeNames.ANDROID_INJECTOR_FACTORY.canonicalName(),
- mapKeyValueElement.getQualifiedName()),
- method);
- }
- }
-
- validateReturnType(method);
-
- // @Binds methods should only have one parameter, but we can't guarantee the order of Processors
- // in javac, so do a basic check for valid form
- if (MoreDaggerElements.isAnnotationPresent(method, TypeNames.BINDS)
- && method.getParameters().size() == 1) {
- validateMapKeyMatchesBindsParameter(annotation, method);
- }
- }
-
- /** Report an error if the method's return type is not {@code AndroidInjector.Factory<?>}. */
- private void validateReturnType(ExecutableElement method) {
- TypeMirror returnType = method.getReturnType();
- DeclaredType requiredReturnType = injectorFactoryOf(types.getWildcardType(null, null));
-
- if (!types.isSameType(returnType, requiredReturnType)) {
- messager.printMessage(
- Kind.ERROR,
- String.format(
- "%s should bind %s, not %s. See https://dagger.dev/android",
- method, requiredReturnType, returnType),
- method);
- }
- }
-
- /**
- * A valid @Binds method could bind an {@code AndroidInjector.Factory} for one type, while giving
- * it a map key of a different type. The return type and parameter type would pass typical @Binds
- * validation, but the map lookup in {@code DispatchingAndroidInjector} would retrieve the wrong
- * injector factory.
- *
- * <pre>{@code
- * {@literal @Binds}
- * {@literal @IntoMap}
- * {@literal @ClassKey(GreenActivity.class)}
- * abstract AndroidInjector.Factory<?> bindBlueActivity(
- * BlueActivityComponent.Builder builder);
- * }</pre>
- */
- private void validateMapKeyMatchesBindsParameter(String annotation, ExecutableElement method) {
- TypeMirror parameterType = getOnlyElement(method.getParameters()).asType();
- AnnotationMirror annotationMirror =
- MoreDaggerElements.getAnnotationMirror(method, SUPPORTED_ANNOTATIONS.get(annotation)).get();
- TypeMirror mapKeyType =
- elements.getTypeElement(injectedTypeFromMapKey(annotationMirror).get()).asType();
- if (!types.isAssignable(parameterType, injectorFactoryOf(mapKeyType))) {
- messager.printMessage(
- Kind.ERROR,
- String.format("%s does not implement AndroidInjector<%s>", parameterType, mapKeyType),
- method,
- annotationMirror);
- }
- }
-
- /** Returns a {@link DeclaredType} for {@code AndroidInjector.Factory<implementationType>}. */
- private DeclaredType injectorFactoryOf(TypeMirror implementationType) {
- return types.getDeclaredType(factoryElement(), implementationType);
- }
-
- private TypeElement factoryElement() {
- return elements.getTypeElement(TypeNames.ANDROID_INJECTOR_FACTORY.canonicalName());
- }
-}
diff --git a/java/dagger/android/processor/AndroidMapKeys.java b/java/dagger/android/processor/AndroidMapKeys.java
index 28da2715a..e3d890e29 100644
--- a/java/dagger/android/processor/AndroidMapKeys.java
+++ b/java/dagger/android/processor/AndroidMapKeys.java
@@ -16,13 +16,9 @@
package dagger.android.processor;
-import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
-
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XAnnotationValue;
import java.util.Optional;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.TypeMirror;
final class AndroidMapKeys {
/**
@@ -30,13 +26,12 @@ final class AndroidMapKeys {
* it's {@link dagger.multibindings.ClassKey}, returns the fully-qualified class name of the
* annotation value. Otherwise returns {@link Optional#empty()}.
*/
- static Optional<String> injectedTypeFromMapKey(AnnotationMirror mapKey) {
- Object mapKeyClass = getAnnotationValue(mapKey, "value").getValue();
- if (mapKeyClass instanceof String) {
- return Optional.of((String) mapKeyClass);
- } else if (mapKeyClass instanceof TypeMirror) {
- TypeElement type = MoreTypes.asTypeElement((TypeMirror) mapKeyClass);
- return Optional.of(type.getQualifiedName().toString());
+ static Optional<String> injectedTypeFromMapKey(XAnnotation mapKey) {
+ XAnnotationValue mapKeyClass = mapKey.getAnnotationValue("value");
+ if (mapKeyClass.hasStringValue()) {
+ return Optional.of(mapKeyClass.asString());
+ } else if (mapKeyClass.hasTypeValue()) {
+ return Optional.of(mapKeyClass.asType().getTypeElement().getQualifiedName());
} else {
return Optional.empty();
}
diff --git a/java/dagger/android/processor/AndroidProcessor.java b/java/dagger/android/processor/AndroidProcessor.java
index 2a8ab345b..beb8d0de7 100644
--- a/java/dagger/android/processor/AndroidProcessor.java
+++ b/java/dagger/android/processor/AndroidProcessor.java
@@ -16,21 +16,15 @@
package dagger.android.processor;
-import static javax.tools.Diagnostic.Kind.ERROR;
import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
-import com.google.auto.common.BasicAnnotationProcessor;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XProcessingStep;
+import androidx.room.compiler.processing.javac.JavacBasicAnnotationProcessor;
import com.google.auto.service.AutoService;
-import com.google.common.base.Ascii;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import java.util.Set;
-import javax.annotation.processing.Filer;
-import javax.annotation.processing.Messager;
import javax.annotation.processing.Processor;
import javax.lang.model.SourceVersion;
-import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
/**
@@ -49,51 +43,22 @@ import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
*/
@IncrementalAnnotationProcessor(ISOLATING)
@AutoService(Processor.class)
-public final class AndroidProcessor extends BasicAnnotationProcessor {
- private static final String FLAG_EXPERIMENTAL_USE_STRING_KEYS =
- "dagger.android.experimentalUseStringKeys";
+public final class AndroidProcessor extends JavacBasicAnnotationProcessor {
+ private final DelegateAndroidProcessor delegate = new DelegateAndroidProcessor();
@Override
- protected Iterable<? extends Step> steps() {
- Filer filer = processingEnv.getFiler();
- Messager messager = processingEnv.getMessager();
- Elements elements = processingEnv.getElementUtils();
- Types types = processingEnv.getTypeUtils();
-
- return ImmutableList.of(
- new AndroidMapKeyValidator(elements, types, messager),
- new ContributesAndroidInjectorGenerator(
- new AndroidInjectorDescriptor.Validator(messager),
- useStringKeys(),
- filer,
- elements,
- processingEnv.getSourceVersion()));
+ public void initialize(XProcessingEnv env) {
+ delegate.initialize(env);
}
- private boolean useStringKeys() {
- if (!processingEnv.getOptions().containsKey(FLAG_EXPERIMENTAL_USE_STRING_KEYS)) {
- return false;
- }
- String flagValue = processingEnv.getOptions().get(FLAG_EXPERIMENTAL_USE_STRING_KEYS);
- if (flagValue == null || Ascii.equalsIgnoreCase(flagValue, "true")) {
- return true;
- } else if (Ascii.equalsIgnoreCase(flagValue, "false")) {
- return false;
- } else {
- processingEnv
- .getMessager()
- .printMessage(
- ERROR,
- String.format(
- "Unknown flag value: %s. %s must be set to either 'true' or 'false'.",
- flagValue, FLAG_EXPERIMENTAL_USE_STRING_KEYS));
- return false;
- }
+ @Override
+ public Iterable<XProcessingStep> processingSteps() {
+ return delegate.processingSteps();
}
@Override
- public Set<String> getSupportedOptions() {
- return ImmutableSet.of(FLAG_EXPERIMENTAL_USE_STRING_KEYS);
+ public final ImmutableSet<String> getSupportedOptions() {
+ return ImmutableSet.of(DelegateAndroidProcessor.FLAG_EXPERIMENTAL_USE_STRING_KEYS);
}
@Override
diff --git a/java/dagger/android/processor/BUILD b/java/dagger/android/processor/BUILD
index d545ef8ef..f70c09102 100644
--- a/java/dagger/android/processor/BUILD
+++ b/java/dagger/android/processor/BUILD
@@ -22,8 +22,7 @@ load(
"DOCLINT_REFERENCES",
"POM_VERSION",
)
-load("//tools:maven.bzl", "pom_file")
-load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library")
+load("//tools:maven.bzl", "gen_maven_artifact")
package(default_visibility = ["//:src"])
@@ -33,29 +32,65 @@ filegroup(
)
java_library(
+ name = "base_processing_step",
+ srcs = ["BaseProcessingStep.java"],
+ deps = [
+ "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/xprocessing",
+ "//third_party/java/guava/base",
+ "//third_party/java/guava/collect",
+ "//third_party/java/javapoet",
+ ],
+)
+
+java_library(
name = "processor",
- srcs = [":srcs"],
+ srcs = glob(
+ ["*.java"],
+ exclude = ["BaseProcessingStep.java"],
+ ),
javacopts = DOCLINT_HTML_AND_SYNTAX + DOCLINT_REFERENCES,
tags = ["maven_coordinates=com.google.dagger:dagger-android-processor:" + POM_VERSION],
deps = [
+ ":base_processing_step",
"//java/dagger:core",
- "//java/dagger/internal/codegen/extension",
+ "//java/dagger/internal/codegen/xprocessing",
"//java/dagger/spi",
- "//third_party/java/auto:common",
"//third_party/java/auto:service",
"//third_party/java/auto:value",
"//third_party/java/guava/base",
"//third_party/java/guava/collect",
"//third_party/java/incap",
"//third_party/java/javapoet",
+ "@maven//:com_google_devtools_ksp_symbol_processing_api",
],
)
-pom_file(
- name = "pom",
- artifact_id = "dagger-android-processor",
+gen_maven_artifact(
+ name = "artifact",
+ artifact_coordinates = "com.google.dagger:dagger-android-processor:" + POM_VERSION,
artifact_name = "Dagger Android Processor",
- targets = [":processor"],
+ artifact_target = ":processor",
+ artifact_target_libs = [
+ "//java/dagger/internal/codegen/xprocessing",
+ "//java/dagger/android/processor:base_processing_step",
+ ],
+ artifact_target_maven_deps = [
+ "com.google.dagger:dagger",
+ "com.google.devtools.ksp:symbol-processing-api",
+ "com.google.guava:guava",
+ "com.squareup:javapoet",
+ "com.google.code.findbugs:jsr305",
+ "com.google.dagger:dagger-spi",
+ "com.google.guava:failureaccess",
+ "com.squareup:kotlinpoet",
+ "net.ltgt.gradle.incap:incap",
+ "org.jetbrains.kotlin:kotlin-stdlib",
+ ],
+ javadoc_root_packages = [
+ "dagger.android.processor",
+ ],
+ javadoc_srcs = [":srcs"],
)
java_plugin(
@@ -64,10 +99,3 @@ java_plugin(
processor_class = "dagger.android.processor.AndroidProcessor",
deps = [":processor"],
)
-
-javadoc_library(
- name = "processor-javadoc",
- srcs = [":srcs"],
- root_packages = ["dagger.android.processor"],
- deps = [":processor"],
-)
diff --git a/java/dagger/android/processor/BaseProcessingStep.java b/java/dagger/android/processor/BaseProcessingStep.java
new file mode 100644
index 000000000..100ded0a0
--- /dev/null
+++ b/java/dagger/android/processor/BaseProcessingStep.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 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.android.processor;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Sets.difference;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
+
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XProcessingStep;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Maps;
+import com.squareup.javapoet.ClassName;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A {@link XProcessingStep} that processes one element at a time and defers any for which {@link
+ * TypeNotPresentException} is thrown.
+ */
+public abstract class BaseProcessingStep implements XProcessingStep {
+ @Override
+ public final ImmutableSet<String> annotations() {
+ return annotationClassNames().stream().map(ClassName::canonicalName).collect(toImmutableSet());
+ }
+
+ // Subclass must ensure all annotated targets are of valid type.
+ @Override
+ public ImmutableSet<XElement> process(
+ XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation) {
+ ImmutableSet.Builder<XElement> deferredElements = ImmutableSet.builder();
+ inverse(elementsByAnnotation)
+ .forEach(
+ (element, annotations) -> {
+ try {
+ process(element, annotations);
+ } catch (TypeNotPresentException e) {
+ deferredElements.add(element);
+ }
+ });
+ return deferredElements.build();
+ }
+
+ /**
+ * Processes one element. If this method throws {@link TypeNotPresentException}, the element will
+ * be deferred until the next round of processing.
+ *
+ * @param annotations the subset of {@link XProcessingStep#annotations()} that annotate {@code
+ * element}
+ */
+ protected abstract void process(XElement element, ImmutableSet<ClassName> annotations);
+
+ private ImmutableMap<XElement, ImmutableSet<ClassName>> inverse(
+ Map<String, ? extends Set<? extends XElement>> elementsByAnnotation) {
+ ImmutableMap<String, ClassName> annotationClassNames =
+ annotationClassNames().stream()
+ .collect(toImmutableMap(ClassName::canonicalName, className -> className));
+ checkState(
+ annotationClassNames.keySet().containsAll(elementsByAnnotation.keySet()),
+ "Unexpected annotations for %s: %s",
+ this.getClass().getCanonicalName(),
+ difference(elementsByAnnotation.keySet(), annotationClassNames.keySet()));
+
+ ImmutableSetMultimap.Builder<XElement, ClassName> builder = ImmutableSetMultimap.builder();
+ elementsByAnnotation.forEach(
+ (annotationName, elementSet) ->
+ elementSet.forEach(
+ element -> builder.put(element, annotationClassNames.get(annotationName))));
+
+ return ImmutableMap.copyOf(Maps.transformValues(builder.build().asMap(), ImmutableSet::copyOf));
+ }
+
+ /** Returns the set of annotations processed by this processing step. */
+ protected abstract Set<ClassName> annotationClassNames();
+}
diff --git a/java/dagger/android/processor/ContributesAndroidInjectorGenerator.java b/java/dagger/android/processor/ContributesAndroidInjectorProcessingStep.java
index f3f4d18ab..9e9ed2b20 100644
--- a/java/dagger/android/processor/ContributesAndroidInjectorGenerator.java
+++ b/java/dagger/android/processor/ContributesAndroidInjectorProcessingStep.java
@@ -16,23 +16,27 @@
package dagger.android.processor;
-import static com.google.auto.common.GeneratedAnnotationSpecs.generatedAnnotationSpec;
+import static androidx.room.compiler.processing.JavaPoetExtKt.addOriginatingElement;
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.UPPER_CAMEL;
import static com.squareup.javapoet.MethodSpec.constructorBuilder;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static com.squareup.javapoet.TypeSpec.classBuilder;
import static com.squareup.javapoet.TypeSpec.interfaceBuilder;
+import static dagger.android.processor.DelegateAndroidProcessor.FLAG_EXPERIMENTAL_USE_STRING_KEYS;
import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
-import static javax.lang.model.util.ElementFilter.methodsIn;
+import static javax.tools.Diagnostic.Kind.ERROR;
-import com.google.auto.common.BasicAnnotationProcessor.Step;
+import androidx.room.compiler.processing.XElement;
+import androidx.room.compiler.processing.XFiler;
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XTypeElement;
+import com.google.common.base.Ascii;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
@@ -41,52 +45,26 @@ import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
-import dagger.android.processor.AndroidInjectorDescriptor.Validator;
-import java.io.IOException;
-import javax.annotation.processing.Filer;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.util.Elements;
+import dagger.internal.codegen.xprocessing.XElements;
/** Generates the implementation specified in {@code ContributesAndroidInjector}. */
-final class ContributesAndroidInjectorGenerator implements Step {
-
+final class ContributesAndroidInjectorProcessingStep extends BaseProcessingStep {
private final AndroidInjectorDescriptor.Validator validator;
- private final Filer filer;
- private final Elements elements;
- private final boolean useStringKeys;
- private final SourceVersion sourceVersion;
-
- ContributesAndroidInjectorGenerator(
- Validator validator,
- boolean useStringKeys,
- Filer filer,
- Elements elements,
- SourceVersion sourceVersion) {
- this.validator = validator;
- this.useStringKeys = useStringKeys;
- this.filer = filer;
- this.elements = elements;
- this.sourceVersion = sourceVersion;
+ private final XProcessingEnv processingEnv;
+
+ ContributesAndroidInjectorProcessingStep(XProcessingEnv processingEnv) {
+ this.processingEnv = processingEnv;
+ this.validator = new AndroidInjectorDescriptor.Validator(processingEnv.getMessager());
}
@Override
- public ImmutableSet<String> annotations() {
- return ImmutableSet.of(TypeNames.CONTRIBUTES_ANDROID_INJECTOR.toString());
+ public ImmutableSet<ClassName> annotationClassNames() {
+ return ImmutableSet.of(TypeNames.CONTRIBUTES_ANDROID_INJECTOR);
}
@Override
- public ImmutableSet<Element> process(ImmutableSetMultimap<String, Element> elementsByAnnotation) {
- ImmutableSet.Builder<Element> deferredElements = ImmutableSet.builder();
- for (ExecutableElement method : methodsIn(elementsByAnnotation.values())) {
- try {
- validator.createIfValid(method).ifPresent(this::generate);
- } catch (TypeNotPresentException e) {
- deferredElements.add(method);
- }
- }
- return deferredElements.build();
+ public void process(XElement element, ImmutableSet<ClassName> annotationNames) {
+ validator.createIfValid(XElements.asMethod(element)).ifPresent(this::generate);
}
private void generate(AndroidInjectorDescriptor descriptor) {
@@ -97,7 +75,7 @@ final class ContributesAndroidInjectorGenerator implements Step {
.peerClass(
Joiner.on('_').join(descriptor.enclosingModule().simpleNames())
+ "_"
- + LOWER_CAMEL.to(UPPER_CAMEL, descriptor.method().getSimpleName().toString()));
+ + LOWER_CAMEL.to(UPPER_CAMEL, XElements.getSimpleName(descriptor.method())));
String baseName = descriptor.injectedType().simpleName();
ClassName subcomponentName = moduleName.nestedClass(baseName + "Subcomponent");
@@ -105,7 +83,6 @@ final class ContributesAndroidInjectorGenerator implements Step {
TypeSpec.Builder module =
classBuilder(moduleName)
- .addOriginatingElement(descriptor.method())
.addAnnotation(
AnnotationSpec.builder(TypeNames.MODULE)
.addMember("subcomponents", "$T.class", subcomponentName)
@@ -114,16 +91,45 @@ final class ContributesAndroidInjectorGenerator implements Step {
.addMethod(bindAndroidInjectorFactory(descriptor, subcomponentFactoryName))
.addType(subcomponent(descriptor, subcomponentName, subcomponentFactoryName))
.addMethod(constructorBuilder().addModifiers(PRIVATE).build());
- generatedAnnotationSpec(elements, sourceVersion, AndroidProcessor.class)
- .ifPresent(module::addAnnotation);
-
- try {
- JavaFile.builder(moduleName.packageName(), module.build())
- .skipJavaLangImports(true)
- .build()
- .writeTo(filer);
- } catch (IOException e) {
- throw new AssertionError(e);
+
+ addOriginatingElement(module, descriptor.method());
+
+ XTypeElement generatedAnnotation = processingEnv.findGeneratedAnnotation();
+ if (generatedAnnotation != null) {
+ module.addAnnotation(
+ AnnotationSpec.builder(generatedAnnotation.getClassName())
+ .addMember(
+ "value", "$S", ClassName.get("dagger.android.processor", "AndroidProcessor"))
+ .build());
+ }
+
+ processingEnv
+ .getFiler()
+ .write(
+ JavaFile.builder(moduleName.packageName(), module.build())
+ .skipJavaLangImports(true)
+ .build(),
+ XFiler.Mode.Isolating);
+ }
+
+ private static boolean useStringKeys(XProcessingEnv processingEnv) {
+ if (!processingEnv.getOptions().containsKey(FLAG_EXPERIMENTAL_USE_STRING_KEYS)) {
+ return false;
+ }
+ String flagValue = processingEnv.getOptions().get(FLAG_EXPERIMENTAL_USE_STRING_KEYS);
+ if (flagValue == null || Ascii.equalsIgnoreCase(flagValue, "true")) {
+ return true;
+ } else if (Ascii.equalsIgnoreCase(flagValue, "false")) {
+ return false;
+ } else {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ ERROR,
+ String.format(
+ "Unknown flag value: %s. %s must be set to either 'true' or 'false'.",
+ flagValue, FLAG_EXPERIMENTAL_USE_STRING_KEYS));
+ return false;
}
}
@@ -142,7 +148,7 @@ final class ContributesAndroidInjectorGenerator implements Step {
}
private AnnotationSpec androidInjectorMapKey(AndroidInjectorDescriptor descriptor) {
- if (useStringKeys) {
+ if (useStringKeys(processingEnv)) {
return AnnotationSpec.builder(TypeNames.ANDROID_INJECTION_KEY)
.addMember("value", "$S", descriptor.injectedType().toString())
.build();
diff --git a/java/dagger/android/processor/DelegateAndroidProcessor.java b/java/dagger/android/processor/DelegateAndroidProcessor.java
new file mode 100644
index 000000000..7c141f041
--- /dev/null
+++ b/java/dagger/android/processor/DelegateAndroidProcessor.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 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.android.processor;
+
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XProcessingEnvConfig;
+import androidx.room.compiler.processing.XProcessingStep;
+import com.google.common.collect.ImmutableList;
+import dagger.BindsInstance;
+import dagger.Component;
+import javax.inject.Singleton;
+
+/** An implementation of Dagger Android processor that is shared between Javac and KSP. */
+final class DelegateAndroidProcessor {
+ static final XProcessingEnvConfig PROCESSING_ENV_CONFIG =
+ new XProcessingEnvConfig.Builder().build();
+ static final String FLAG_EXPERIMENTAL_USE_STRING_KEYS =
+ "dagger.android.experimentalUseStringKeys";
+
+ private XProcessingEnv env;
+
+ public void initialize(XProcessingEnv env) {
+ this.env = env;
+ }
+
+ public ImmutableList<XProcessingStep> processingSteps() {
+ return ImmutableList.of(
+ new AndroidMapKeyProcessingStep(env), new ContributesAndroidInjectorProcessingStep(env));
+ }
+
+ @Singleton
+ @Component
+ interface Injector {
+ void inject(DelegateAndroidProcessor delegateAndroidProcessor);
+
+ @Component.Factory
+ interface Factory {
+ Injector create(@BindsInstance XProcessingEnv env);
+ }
+ }
+}
diff --git a/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java b/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java
index bcc8e5a1e..22270381c 100644
--- a/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java
+++ b/java/dagger/android/processor/DuplicateAndroidInjectorsChecker.java
@@ -16,34 +16,34 @@
package dagger.android.processor;
-import static com.google.auto.common.AnnotationMirrors.getAnnotatedAnnotations;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.android.processor.AndroidMapKeys.injectedTypeFromMapKey;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
import static javax.tools.Diagnostic.Kind.ERROR;
-import com.google.auto.common.MoreTypes;
+import androidx.room.compiler.processing.XAnnotation;
+import androidx.room.compiler.processing.XType;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
-import dagger.MapKey;
-import dagger.model.Binding;
-import dagger.model.BindingGraph;
-import dagger.model.BindingKind;
-import dagger.model.Key;
-import dagger.spi.BindingGraphPlugin;
-import dagger.spi.DiagnosticReporter;
+import dagger.internal.codegen.xprocessing.DaggerElements;
+import dagger.internal.codegen.xprocessing.XElements;
+import dagger.internal.codegen.xprocessing.XTypes;
+import dagger.spi.model.Binding;
+import dagger.spi.model.BindingGraph;
+import dagger.spi.model.BindingGraphPlugin;
+import dagger.spi.model.BindingKind;
+import dagger.spi.model.DaggerProcessingEnv;
+import dagger.spi.model.DiagnosticReporter;
+import dagger.spi.model.Key;
import java.util.Formatter;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
/**
* Validates that the two maps that {@code DispatchingAndroidInjector} injects have logically
@@ -53,6 +53,13 @@ import javax.lang.model.type.TypeMirror;
*/
@AutoService(BindingGraphPlugin.class)
public final class DuplicateAndroidInjectorsChecker implements BindingGraphPlugin {
+ private DaggerProcessingEnv processingEnv;
+
+ @Override
+ public void init(DaggerProcessingEnv processingEnv, Map<String, String> options) {
+ this.processingEnv = processingEnv;
+ }
+
@Override
public void visitGraph(BindingGraph graph, DiagnosticReporter diagnosticReporter) {
for (Binding binding : graph.bindings()) {
@@ -64,7 +71,10 @@ public final class DuplicateAndroidInjectorsChecker implements BindingGraphPlugi
private boolean isDispatchingAndroidInjector(Binding binding) {
Key key = binding.key();
- return MoreDaggerTypes.isTypeOf(TypeNames.DISPATCHING_ANDROID_INJECTOR, key.type())
+
+ return XTypes.isTypeOf(
+ DaggerElements.toXProcessing(key.type(), processingEnv),
+ TypeNames.DISPATCHING_ANDROID_INJECTOR)
&& !key.qualifier().isPresent();
}
@@ -79,7 +89,7 @@ public final class DuplicateAndroidInjectorsChecker implements BindingGraphPlugi
ImmutableListMultimap.Builder<String, Binding> mapKeyIndex = ImmutableListMultimap.builder();
for (Binding injectorFactory : injectorFactories) {
- AnnotationMirror mapKey = mapKey(injectorFactory).get();
+ XAnnotation mapKey = mapKey(injectorFactory).get();
Optional<String> injectedType = injectedTypeFromMapKey(mapKey);
if (injectedType.isPresent()) {
mapKeyIndex.put(injectedType.get(), injectorFactory);
@@ -113,21 +123,26 @@ public final class DuplicateAndroidInjectorsChecker implements BindingGraphPlugi
.filter(requestedBinding -> requestedBinding.kind().equals(BindingKind.MULTIBOUND_MAP))
.filter(
requestedBinding -> {
- TypeMirror valueType =
- MoreTypes.asDeclared(requestedBinding.key().type()).getTypeArguments().get(1);
- if (!MoreDaggerTypes.isTypeOf(TypeNames.PROVIDER, valueType)
- || !valueType.getKind().equals(TypeKind.DECLARED)) {
+ XType valueType =
+ DaggerElements.toXProcessing(requestedBinding.key().type(), processingEnv)
+ .getTypeArguments()
+ .get(1);
+ if (!XTypes.isTypeOf(valueType, TypeNames.PROVIDER)
+ || !XTypes.isDeclared(valueType)) {
return false;
}
- TypeMirror providedType = MoreTypes.asDeclared(valueType).getTypeArguments().get(0);
- return MoreDaggerTypes.isTypeOf(TypeNames.ANDROID_INJECTOR_FACTORY, providedType);
+ XType providedType = valueType.getTypeArguments().get(0);
+ return XTypes.isTypeOf(providedType, TypeNames.ANDROID_INJECTOR_FACTORY);
});
}
- private Optional<AnnotationMirror> mapKey(Binding binding) {
+ private Optional<XAnnotation> mapKey(Binding binding) {
return binding
.bindingElement()
- .map(bindingElement -> getAnnotatedAnnotations(bindingElement, MapKey.class))
+ .map(
+ bindingElement ->
+ XElements.getAnnotatedAnnotations(
+ DaggerElements.toXProcessing(bindingElement, processingEnv), TypeNames.MAP_KEY))
.flatMap(
annotations ->
annotations.isEmpty()
diff --git a/java/dagger/android/processor/KspAndroidProcessor.java b/java/dagger/android/processor/KspAndroidProcessor.java
new file mode 100644
index 000000000..7fe4f52d8
--- /dev/null
+++ b/java/dagger/android/processor/KspAndroidProcessor.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 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.android.processor;
+
+import androidx.room.compiler.processing.XProcessingEnv;
+import androidx.room.compiler.processing.XProcessingStep;
+import androidx.room.compiler.processing.ksp.KspBasicAnnotationProcessor;
+import com.google.auto.service.AutoService;
+import com.google.devtools.ksp.processing.SymbolProcessor;
+import com.google.devtools.ksp.processing.SymbolProcessorEnvironment;
+import com.google.devtools.ksp.processing.SymbolProcessorProvider;
+
+/** Ksp Processor for verifying usage of {@code dagger.android} code. */
+final class KspAndroidProcessor extends KspBasicAnnotationProcessor {
+ private final DelegateAndroidProcessor delegate = new DelegateAndroidProcessor();
+
+ private KspAndroidProcessor(SymbolProcessorEnvironment symbolProcessorEnvironment) {
+ super(symbolProcessorEnvironment, DelegateAndroidProcessor.PROCESSING_ENV_CONFIG);
+ }
+
+ @Override
+ public void initialize(XProcessingEnv env) {
+ delegate.initialize(env);
+ }
+
+ @Override
+ public Iterable<XProcessingStep> processingSteps() {
+ return delegate.processingSteps();
+ }
+
+ /** Provides the {@link KspAndroidProcessor}. */
+ @AutoService(SymbolProcessorProvider.class)
+ public static final class Provider implements SymbolProcessorProvider {
+ @Override
+ public SymbolProcessor create(SymbolProcessorEnvironment symbolProcessorEnvironment) {
+ return new KspAndroidProcessor(symbolProcessorEnvironment);
+ }
+ }
+}
diff --git a/java/dagger/android/processor/MoreDaggerElements.java b/java/dagger/android/processor/MoreDaggerElements.java
deleted file mode 100644
index 572caa31a..000000000
--- a/java/dagger/android/processor/MoreDaggerElements.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2021 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.android.processor;
-
-import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
-
-import com.google.auto.common.MoreElements;
-import com.google.common.collect.ImmutableSet;
-import com.squareup.javapoet.ClassName;
-import java.util.Optional;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-
-// TODO(bcorso): Dedupe with dagger/internal/codegen/langmodel/DaggerElements.java?
-// TODO(bcorso): Contribute upstream to auto common?
-/** Similar to auto common, but uses {@link ClassName} rather than {@link Class}. */
-final class MoreDaggerElements {
- /**
- * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
- * AnnotationMirror#getAnnotationType() annotation type} has the same canonical name as that of
- * {@code annotationClass}. This method is a safer alternative to calling {@link
- * Element#getAnnotation} and checking for {@code null} as it avoids any interaction with
- * annotation proxies.
- */
- public static boolean isAnnotationPresent(Element element, ClassName annotationName) {
- return getAnnotationMirror(element, annotationName).isPresent();
- }
-
- /**
- * Returns an {@link AnnotationMirror} for the annotation of type {@code annotationClass} on
- * {@code element}, or {@link Optional#empty()} if no such annotation exists. This method is a
- * safer alternative to calling {@link Element#getAnnotation} as it avoids any interaction with
- * annotation proxies.
- */
- public static Optional<AnnotationMirror> getAnnotationMirror(
- Element element, ClassName annotationName) {
- String annotationClassName = annotationName.canonicalName();
- for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
- TypeElement annotationTypeElement =
- MoreElements.asType(annotationMirror.getAnnotationType().asElement());
- if (annotationTypeElement.getQualifiedName().contentEquals(annotationClassName)) {
- return Optional.of(annotationMirror);
- }
- }
- return Optional.empty();
- }
-
- public static ImmutableSet<AnnotationMirror> getAnnotatedAnnotations(
- Element element, ClassName annotationName) {
- return element.getAnnotationMirrors().stream()
- .filter(input -> isAnnotationPresent(input.getAnnotationType().asElement(), annotationName))
- .collect(toImmutableSet());
- }
-
- private MoreDaggerElements() {}
-}
diff --git a/java/dagger/android/processor/MoreDaggerTypes.java b/java/dagger/android/processor/MoreDaggerTypes.java
deleted file mode 100644
index 4bde405e1..000000000
--- a/java/dagger/android/processor/MoreDaggerTypes.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2021 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.android.processor;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.auto.common.MoreElements;
-import com.squareup.javapoet.ArrayTypeName;
-import com.squareup.javapoet.ClassName;
-import com.squareup.javapoet.TypeName;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.ErrorType;
-import javax.lang.model.type.NoType;
-import javax.lang.model.type.PrimitiveType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-import javax.lang.model.util.SimpleTypeVisitor8;
-
-// TODO(bcorso): Dedupe with dagger/internal/codegen/langmodel/DaggerTypes.java?
-// TODO(bcorso): Contribute upstream to auto common?
-/** Similar to auto common, but uses {@link ClassName} rather than {@link Class}. */
-final class MoreDaggerTypes {
-
- /**
- * Returns true if the raw type underlying the given {@link TypeMirror} represents the same raw
- * type as the given {@link Class} and throws an IllegalArgumentException if the {@link
- * TypeMirror} does not represent a type that can be referenced by a {@link Class}
- */
- public static boolean isTypeOf(final TypeName typeName, TypeMirror type) {
- checkNotNull(typeName);
- return type.accept(new IsTypeOf(typeName), null);
- }
-
- private static final class IsTypeOf extends SimpleTypeVisitor8<Boolean, Void> {
- private final TypeName typeName;
-
- IsTypeOf(TypeName typeName) {
- this.typeName = typeName;
- }
-
- @Override
- protected Boolean defaultAction(TypeMirror type, Void ignored) {
- throw new IllegalArgumentException(type + " cannot be represented as a Class<?>.");
- }
-
- @Override
- public Boolean visitNoType(NoType noType, Void p) {
- if (noType.getKind().equals(TypeKind.VOID)) {
- return typeName.equals(TypeName.VOID);
- }
- throw new IllegalArgumentException(noType + " cannot be represented as a Class<?>.");
- }
-
- @Override
- public Boolean visitError(ErrorType errorType, Void p) {
- return false;
- }
-
- @Override
- public Boolean visitPrimitive(PrimitiveType type, Void p) {
- switch (type.getKind()) {
- case BOOLEAN:
- return typeName.equals(TypeName.BOOLEAN);
- case BYTE:
- return typeName.equals(TypeName.BYTE);
- case CHAR:
- return typeName.equals(TypeName.CHAR);
- case DOUBLE:
- return typeName.equals(TypeName.DOUBLE);
- case FLOAT:
- return typeName.equals(TypeName.FLOAT);
- case INT:
- return typeName.equals(TypeName.INT);
- case LONG:
- return typeName.equals(TypeName.LONG);
- case SHORT:
- return typeName.equals(TypeName.SHORT);
- default:
- throw new IllegalArgumentException(type + " cannot be represented as a Class<?>.");
- }
- }
-
- @Override
- public Boolean visitArray(ArrayType array, Void p) {
- return (typeName instanceof ArrayTypeName)
- && isTypeOf(((ArrayTypeName) typeName).componentType, array.getComponentType());
- }
-
- @Override
- public Boolean visitDeclared(DeclaredType type, Void ignored) {
- TypeElement typeElement = MoreElements.asType(type.asElement());
- return (typeName instanceof ClassName)
- && typeElement.getQualifiedName().contentEquals(((ClassName) typeName).canonicalName());
- }
- }
-
- private MoreDaggerTypes() {}
-}