diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2021-06-19 12:01:10 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2021-06-19 12:01:10 +0000 |
commit | 296cf8aac90a851231a2d0d8a4485c9b11a6ea0e (patch) | |
tree | c5dc00f1b736545ca86950849544f0bb7d913729 | |
parent | a5ab264b9ddc248027aa200914c8dfa6089c6b35 (diff) | |
parent | d542bcf5c7c83f8c458763a5f6bb3356e07e80dc (diff) | |
download | auto-android12-mainline-media-swcodec-release.tar.gz |
Snap for 7474514 from d542bcf5c7c83f8c458763a5f6bb3356e07e80dc to mainline-media-swcodec-releaseandroid-mainline-12.0.0_r91android-mainline-12.0.0_r75android-mainline-12.0.0_r47android-mainline-12.0.0_r30android-mainline-12.0.0_r13android-mainline-12.0.0_r120android-mainline-12.0.0_r105android12-mainline-media-swcodec-release
Change-Id: I7dcddb2949da5ba208118bf5cc3d4f3ceac117a4
42 files changed, 1130 insertions, 499 deletions
@@ -1,3 +1,17 @@ package { - default_visibility: [":__subpackages__"] + default_visibility: [":__subpackages__"], + default_applicable_licenses: ["external_auto_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "external_auto_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "LICENSE", + ], } @@ -11,7 +11,7 @@ third_party { type: GIT value: "https://github.com/google/auto" } - version: "auto-value-1.7.3" - last_upgrade_date { year: 2020 month: 6 day: 12 } + version: "auto-value-1.7.4" + last_upgrade_date { year: 2020 month: 11 day: 18 } license_type: NOTICE } diff --git a/android-annotation-stubs/Android.bp b/android-annotation-stubs/Android.bp index a2bd9e98..3e0be03d 100644 --- a/android-annotation-stubs/Android.bp +++ b/android-annotation-stubs/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "external_auto_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["external_auto_license"], +} + java_library_host { name: "auto_android_annotation_stubs", srcs: ["src/**/*.java"], diff --git a/common/Android.bp b/common/Android.bp index 279b90d9..25f70998 100644 --- a/common/Android.bp +++ b/common/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "external_auto_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["external_auto_license"], +} + java_library_host { name: "auto_common", srcs: ["src/main/java/**/*.java"], diff --git a/common/pom.xml b/common/pom.xml index 7d1c8773..e4a23e02 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -26,7 +26,7 @@ <groupId>com.google.auto</groupId> <artifactId>auto-common</artifactId> - <version>HEAD-SNAPSHOT</version> + <version>0.11</version> <name>Auto Common Libraries</name> <description> Common utilities for creating annotation processors. @@ -36,7 +36,7 @@ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> - <guava.version>27.0.1-jre</guava.version> + <guava.version>29.0-jre</guava.version> <truth.version>1.0.1</truth.version> </properties> @@ -75,7 +75,7 @@ If you use JavaPoet, you can use GeneratedAnnotationSpecs. --> <groupId>com.squareup</groupId> <artifactId>javapoet</artifactId> - <version>1.9.0</version> + <version>1.13.0</version> <optional>true</optional> </dependency> @@ -107,7 +107,7 @@ <dependency> <groupId>org.eclipse.jdt</groupId> <artifactId>ecj</artifactId> - <version>3.20.0</version> + <version>3.22.0</version> <scope>test</scope> </dependency> </dependencies> diff --git a/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java b/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java index d3f67aa5..375a4cb8 100644 --- a/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java +++ b/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java @@ -17,12 +17,14 @@ package com.google.auto.common; import static com.google.auto.common.MoreElements.asExecutable; import static com.google.auto.common.MoreElements.asPackage; -import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.auto.common.SuperficialValidation.validateElement; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Iterables.transform; import static com.google.common.collect.Multimaps.filterKeys; +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; import static javax.lang.model.element.ElementKind.PACKAGE; import static javax.tools.Diagnostic.Kind.ERROR; @@ -30,8 +32,10 @@ import com.google.common.base.Ascii; import com.google.common.base.Optional; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.SetMultimap; import com.google.common.collect.Sets; @@ -57,9 +61,9 @@ import javax.lang.model.util.SimpleElementVisitor8; * An abstract {@link Processor} implementation that defers processing of {@link Element}s to later * rounds if they cannot be processed. * - * <p>Subclasses put their processing logic in {@link ProcessingStep} implementations. The steps are - * passed to the processor by returning them in the {@link #initSteps()} method, and can access the - * {@link ProcessingEnvironment} using {@link #processingEnv}. + * <p>Subclasses put their processing logic in {@link Step} implementations. The steps are passed to + * the processor by returning them in the {@link #steps()} method, and can access the {@link + * ProcessingEnvironment} using {@link #processingEnv}. * * <p>Any logic that needs to happen once per round can be specified by overriding {@link * #postRound(RoundEnvironment)}. @@ -67,8 +71,8 @@ import javax.lang.model.util.SimpleElementVisitor8; * <h3>Ill-formed elements are deferred</h3> * * Any annotated element whose nearest enclosing type is not well-formed is deferred, and not passed - * to any {@code ProcessingStep}. This helps processors to avoid many common pitfalls, such as - * {@link ErrorType} instances, {@link ClassCastException}s and badly coerced types. + * to any {@code Step}. This helps processors to avoid many common pitfalls, such as {@link + * ErrorType} instances, {@link ClassCastException}s and badly coerced types. * * <p>A non-package element is considered well-formed if its type, type parameters, parameters, * default values, supertypes, annotations, and enclosed elements are. Package elements are treated @@ -80,11 +84,11 @@ import javax.lang.model.util.SimpleElementVisitor8; * because the element will never be fully complete. All such compilations will fail with an error * message on the offending type that describes the issue. * - * <h3>Each {@code ProcessingStep} can defer elements</h3> + * <h3>Each {@code Step} can defer elements</h3> * - * <p>Each {@code ProcessingStep} can defer elements by including them in the set returned by {@link - * ProcessingStep#process(SetMultimap)}; elements deferred by a step will be passed back to that - * step in a later round of processing. + * <p>Each {@code Step} can defer elements by including them in the set returned by {@link + * Step#process(ImmutableSetMultimap)}; elements deferred by a step will be passed back to that step + * in a later round of processing. * * <p>This feature is useful when one processor may depend on code generated by another, independent * processor, in a way that isn't caught by the well-formedness check described above. For example, @@ -102,26 +106,42 @@ import javax.lang.model.util.SimpleElementVisitor8; public abstract class BasicAnnotationProcessor extends AbstractProcessor { private final Set<ElementName> deferredElementNames = new LinkedHashSet<>(); - private final SetMultimap<ProcessingStep, ElementName> elementsDeferredBySteps = + private final SetMultimap<Step, ElementName> elementsDeferredBySteps = LinkedHashMultimap.create(); private Elements elements; private Messager messager; - private ImmutableList<? extends ProcessingStep> steps; + private ImmutableList<? extends Step> steps; @Override public final synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); this.elements = processingEnv.getElementUtils(); this.messager = processingEnv.getMessager(); - this.steps = ImmutableList.copyOf(initSteps()); + this.steps = ImmutableList.copyOf(steps()); } /** * Creates {@linkplain ProcessingStep processing steps} for this processor. {@link #processingEnv} * is guaranteed to be set when this method is invoked. + * + * @deprecated Implement {@link #steps()} instead. + */ + @Deprecated + protected Iterable<? extends ProcessingStep> initSteps() { + throw new AssertionError("If steps() is not implemented, initSteps() must be."); + } + + /** + * Creates {@linkplain Step processing steps} for this processor. {@link #processingEnv} is + * guaranteed to be set when this method is invoked. + * + * <p>Note: If you are migrating some steps from {@link ProcessingStep} to {@link Step}, then you + * can call {@link #asStep(ProcessingStep)} on any unmigrated steps. */ - protected abstract Iterable<? extends ProcessingStep> initSteps(); + protected Iterable<? extends Step> steps() { + return Iterables.transform(initSteps(), BasicAnnotationProcessor::asStep); + } /** * An optional hook for logic to be executed at the end of each round. @@ -138,26 +158,30 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor { } } - private ImmutableSet<? extends Class<? extends Annotation>> getSupportedAnnotationClasses() { + private ImmutableSet<TypeElement> getSupportedAnnotationTypeElements() { checkState(steps != null); - ImmutableSet.Builder<Class<? extends Annotation>> builder = ImmutableSet.builder(); - for (ProcessingStep step : steps) { - builder.addAll(step.annotations()); - } - return builder.build(); + return steps.stream() + .flatMap(step -> getSupportedAnnotationTypeElements(step).stream()) + .collect(collectingAndThen(toList(), ImmutableSet::copyOf)); + } + + private ImmutableSet<TypeElement> getSupportedAnnotationTypeElements(Step step) { + return step.annotations().stream() + .map(elements::getTypeElement) + .filter(Objects::nonNull) + .collect(collectingAndThen(toList(), ImmutableSet::copyOf)); } /** - * Returns the set of supported annotation types as a collected from registered {@linkplain - * ProcessingStep processing steps}. + * Returns the set of supported annotation types as collected from registered {@linkplain Step + * processing steps}. */ @Override public final ImmutableSet<String> getSupportedAnnotationTypes() { - ImmutableSet.Builder<String> builder = ImmutableSet.builder(); - for (Class<? extends Annotation> annotationClass : getSupportedAnnotationClasses()) { - builder.add(annotationClass.getCanonicalName()); - } - return builder.build(); + checkState(steps != null); + return steps.stream() + .flatMap(step -> step.annotations().stream()) + .collect(collectingAndThen(toList(), ImmutableSet::copyOf)); } @Override @@ -189,17 +213,19 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor { } /** Processes the valid elements, including those previously deferred by each step. */ - private void process(ImmutableSetMultimap<Class<? extends Annotation>, Element> validElements) { - for (ProcessingStep step : steps) { - ImmutableSetMultimap<Class<? extends Annotation>, Element> stepElements = - new ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element>() - .putAll(indexByAnnotation(elementsDeferredBySteps.get(step), step.annotations())) - .putAll(filterKeys(validElements, Predicates.<Object>in(step.annotations()))) + private void process(ImmutableSetMultimap<TypeElement, Element> validElements) { + for (Step step : steps) { + ImmutableSet<TypeElement> annotationTypes = getSupportedAnnotationTypeElements(step); + ImmutableSetMultimap<TypeElement, Element> stepElements = + new ImmutableSetMultimap.Builder<TypeElement, Element>() + .putAll(indexByAnnotation(elementsDeferredBySteps.get(step), annotationTypes)) + .putAll(filterKeys(validElements, Predicates.in(annotationTypes))) .build(); if (stepElements.isEmpty()) { elementsDeferredBySteps.removeAll(step); } else { - Set<? extends Element> rejectedElements = step.process(stepElements); + Set<? extends Element> rejectedElements = + step.process(toClassNameKeyedMultimap(stepElements)); elementsDeferredBySteps.replaceValues( step, transform(rejectedElements, ElementName::forAnnotatedElement)); } @@ -233,43 +259,39 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor { * Returns the valid annotated elements contained in all of the deferred elements. If none are * found for a deferred element, defers it again. */ - private ImmutableSetMultimap<Class<? extends Annotation>, Element> validElements( - RoundEnvironment roundEnv) { + private ImmutableSetMultimap<TypeElement, Element> validElements(RoundEnvironment roundEnv) { ImmutableSet<ElementName> prevDeferredElementNames = ImmutableSet.copyOf(deferredElementNames); deferredElementNames.clear(); - ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element> - deferredElementsByAnnotationBuilder = ImmutableSetMultimap.builder(); + ImmutableSetMultimap.Builder<TypeElement, Element> deferredElementsByAnnotationBuilder = + ImmutableSetMultimap.builder(); for (ElementName deferredElementName : prevDeferredElementNames) { Optional<? extends Element> deferredElement = deferredElementName.getElement(elements); if (deferredElement.isPresent()) { findAnnotatedElements( deferredElement.get(), - getSupportedAnnotationClasses(), + getSupportedAnnotationTypeElements(), deferredElementsByAnnotationBuilder); } else { deferredElementNames.add(deferredElementName); } } - ImmutableSetMultimap<Class<? extends Annotation>, Element> deferredElementsByAnnotation = + ImmutableSetMultimap<TypeElement, Element> deferredElementsByAnnotation = deferredElementsByAnnotationBuilder.build(); - ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element> validElements = + ImmutableSetMultimap.Builder<TypeElement, Element> validElements = ImmutableSetMultimap.builder(); Set<ElementName> validElementNames = new LinkedHashSet<>(); // Look at the elements we've found and the new elements from this round and validate them. - for (Class<? extends Annotation> annotationClass : getSupportedAnnotationClasses()) { - // This should just call roundEnv.getElementsAnnotatedWith(Class) directly, but there is a bug - // in some versions of eclipse that cause that method to crash. - TypeElement annotationType = elements.getTypeElement(annotationClass.getCanonicalName()); + for (TypeElement annotationType : getSupportedAnnotationTypeElements()) { Set<? extends Element> roundElements = (annotationType == null) - ? ImmutableSet.<Element>of() + ? ImmutableSet.of() : roundEnv.getElementsAnnotatedWith(annotationType); - ImmutableSet<Element> prevRoundElements = deferredElementsByAnnotation.get(annotationClass); + ImmutableSet<Element> prevRoundElements = deferredElementsByAnnotation.get(annotationType); for (Element element : Sets.union(roundElements, prevRoundElements)) { ElementName elementName = ElementName.forAnnotatedElement(element); boolean isValidElement = @@ -278,7 +300,7 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor { && validateElement( element.getKind().equals(PACKAGE) ? element : getEnclosingType(element))); if (isValidElement) { - validElements.put(annotationClass, element); + validElements.put(annotationType, element); validElementNames.add(elementName); } else { deferredElementNames.add(elementName); @@ -289,15 +311,14 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor { return validElements.build(); } - private ImmutableSetMultimap<Class<? extends Annotation>, Element> indexByAnnotation( - Set<ElementName> annotatedElements, - Set<? extends Class<? extends Annotation>> annotationClasses) { - ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element> deferredElements = + private ImmutableSetMultimap<TypeElement, Element> indexByAnnotation( + Set<ElementName> annotatedElements, ImmutableSet<TypeElement> annotationTypes) { + ImmutableSetMultimap.Builder<TypeElement, Element> deferredElements = ImmutableSetMultimap.builder(); for (ElementName elementName : annotatedElements) { Optional<? extends Element> element = elementName.getElement(elements); if (element.isPresent()) { - findAnnotatedElements(element.get(), annotationClasses, deferredElements); + findAnnotatedElements(element.get(), annotationTypes, deferredElements); } } return deferredElements.build(); @@ -305,8 +326,8 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor { /** * Adds {@code element} and its enclosed elements to {@code annotatedElements} if they are - * annotated with any annotations in {@code annotationClasses}. Does not traverse to member types - * of {@code element}, so that if {@code Outer} is passed in the example below, looking for + * annotated with any annotations in {@code annotationTypes}. Does not traverse to member types of + * {@code element}, so that if {@code Outer} is passed in the example below, looking for * {@code @X}, then {@code Outer}, {@code Outer.foo}, and {@code Outer.foo()} will be added to the * multimap, but neither {@code Inner} nor its members will. * @@ -323,27 +344,33 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor { */ private static void findAnnotatedElements( Element element, - Set<? extends Class<? extends Annotation>> annotationClasses, - ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element> annotatedElements) { + ImmutableSet<TypeElement> annotationTypes, + ImmutableSetMultimap.Builder<TypeElement, Element> annotatedElements) { for (Element enclosedElement : element.getEnclosedElements()) { if (!enclosedElement.getKind().isClass() && !enclosedElement.getKind().isInterface()) { - findAnnotatedElements(enclosedElement, annotationClasses, annotatedElements); + findAnnotatedElements(enclosedElement, annotationTypes, annotatedElements); } } // element.getEnclosedElements() does NOT return parameter elements if (element instanceof ExecutableElement) { for (Element parameterElement : asExecutable(element).getParameters()) { - findAnnotatedElements(parameterElement, annotationClasses, annotatedElements); + findAnnotatedElements(parameterElement, annotationTypes, annotatedElements); } } - for (Class<? extends Annotation> annotationClass : annotationClasses) { - if (isAnnotationPresent(element, annotationClass)) { - annotatedElements.put(annotationClass, element); + for (TypeElement annotationType : annotationTypes) { + if (isAnnotationPresent(element, annotationType)) { + annotatedElements.put(annotationType, element); } } } + private static boolean isAnnotationPresent(Element element, TypeElement annotationType) { + return element.getAnnotationMirrors().stream() + .anyMatch( + mirror -> MoreTypes.asTypeElement(mirror.getAnnotationType()).equals(annotationType)); + } + /** * Returns the nearest enclosing {@link TypeElement} to the current element, throwing an {@link * IllegalArgumentException} if the provided {@link Element} is a {@link PackageElement} or is @@ -371,12 +398,61 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor { null); } + private static ImmutableSetMultimap<String, Element> toClassNameKeyedMultimap( + SetMultimap<TypeElement, Element> elements) { + ImmutableSetMultimap.Builder<String, Element> builder = ImmutableSetMultimap.builder(); + elements + .asMap() + .forEach( + (annotation, element) -> + builder.putAll(annotation.getQualifiedName().toString(), element)); + return builder.build(); + } + + /** + * Wraps the passed {@link ProcessingStep} in a {@link Step}. This is a convenience method to + * allow incremental migration to a String-based API. This method can be used to return a not yet + * converted {@link ProcessingStep} from {@link BasicAnnotationProcessor#steps()}. + */ + protected static Step asStep(ProcessingStep processingStep) { + return new ProcessingStepAsStep(processingStep); + } + /** * The unit of processing logic that runs under the guarantee that all elements are complete and * well-formed. A step may reject elements that are not ready for processing but may be at a later * round. */ + public interface Step { + + /** + * The set of fully-qualified annotation type names processed by this step. + * + * <p>Warning: If the returned names are not names of annotations, they'll be ignored. + */ + Set<String> annotations(); + + /** + * The implementation of processing logic for the step. It is guaranteed that the keys in {@code + * elementsByAnnotation} will be a subset of the set returned by {@link #annotations()}. + * + * @return the elements (a subset of the values of {@code elementsByAnnotation}) that this step + * is unable to process, possibly until a later processing round. These elements will be + * passed back to this step at the next round of processing. + */ + Set<? extends Element> process(ImmutableSetMultimap<String, Element> elementsByAnnotation); + } + + /** + * The unit of processing logic that runs under the guarantee that all elements are complete and + * well-formed. A step may reject elements that are not ready for processing but may be at a later + * round. + * + * @deprecated Implement {@link Step} instead. See {@link BasicAnnotationProcessor#steps()}. + */ + @Deprecated public interface ProcessingStep { + /** The set of annotation types processed by this step. */ Set<? extends Class<? extends Annotation>> annotations(); @@ -392,6 +468,46 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor { SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation); } + private static class ProcessingStepAsStep implements Step { + + private final ProcessingStep processingStep; + private final ImmutableMap<String, Class<? extends Annotation>> annotationsByName; + + ProcessingStepAsStep(ProcessingStep processingStep) { + this.processingStep = processingStep; + this.annotationsByName = + processingStep.annotations().stream() + .collect( + collectingAndThen( + toMap( + Class::getCanonicalName, (Class<? extends Annotation> aClass) -> aClass), + ImmutableMap::copyOf)); + } + + @Override + public Set<String> annotations() { + return annotationsByName.keySet(); + } + + @Override + public Set<? extends Element> process( + ImmutableSetMultimap<String, Element> elementsByAnnotation) { + return processingStep.process(toClassKeyedMultimap(elementsByAnnotation)); + } + + private ImmutableSetMultimap<Class<? extends Annotation>, Element> toClassKeyedMultimap( + SetMultimap<String, Element> elements) { + ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element> builder = + ImmutableSetMultimap.builder(); + elements + .asMap() + .forEach( + (annotation, annotatedElements) -> + builder.putAll(annotationsByName.get(annotation), annotatedElements)); + return builder.build(); + } + } + /** * A package or type name. * diff --git a/common/src/main/java/com/google/auto/common/SuperficialValidation.java b/common/src/main/java/com/google/auto/common/SuperficialValidation.java index dddb1be9..5ef4dbf2 100644 --- a/common/src/main/java/com/google/auto/common/SuperficialValidation.java +++ b/common/src/main/java/com/google/auto/common/SuperficialValidation.java @@ -17,6 +17,7 @@ package com.google.auto.common; import java.util.List; import java.util.Map; +import java.util.stream.StreamSupport; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValueVisitor; @@ -46,13 +47,12 @@ import javax.lang.model.util.SimpleTypeVisitor8; * @author Gregory Kick */ public final class SuperficialValidation { + /** + * Returns true if all of the given elements return true from {@link #validateElement(Element)}. + */ public static boolean validateElements(Iterable<? extends Element> elements) { - for (Element element : elements) { - if (!validateElement(element)) { - return false; - } - } - return true; + return StreamSupport.stream(elements.spliterator(), false) + .allMatch(SuperficialValidation::validateElement); } private static final ElementVisitor<Boolean, Void> ELEMENT_VALIDATING_VISITOR = @@ -94,6 +94,12 @@ public final class SuperficialValidation { } }; + /** + * Returns true if all types referenced by the given element are defined. The exact meaning of + * this depends on the kind of element. For packages, it means that all annotations on the package + * are fully defined. For other element kinds, it means that types referenced by the element, + * anything it contains, and any of its annotations element are all defined. + */ public static boolean validateElement(Element element) { return element.accept(ELEMENT_VALIDATING_VISITOR, null); } @@ -163,7 +169,13 @@ public final class SuperficialValidation { } }; - private static boolean validateType(TypeMirror type) { + /** + * Returns true if the given type is fully defined. This means that the type itself is defined, as + * are any types it references, such as any type arguments or type bounds. For an {@link + * ExecutableType}, the parameter and return types must be fully defined, as must types declared + * in a {@code throws} clause or in the bounds of any type parameters. + */ + public static boolean validateType(TypeMirror type) { return type.accept(TYPE_VALIDATING_VISITOR, null); } @@ -182,17 +194,14 @@ public final class SuperficialValidation { && validateAnnotationValues(annotationMirror.getElementValues()); } - @SuppressWarnings("unused") private static boolean validateAnnotationValues( Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap) { - for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> valueEntry : - valueMap.entrySet()) { - TypeMirror expectedType = valueEntry.getKey().getReturnType(); - if (!validateAnnotationValue(valueEntry.getValue(), expectedType)) { - return false; - } - } - return true; + return valueMap.entrySet().stream() + .allMatch( + valueEntry -> { + TypeMirror expectedType = valueEntry.getKey().getReturnType(); + return validateAnnotationValue(valueEntry.getValue(), expectedType); + }); } private static final AnnotationValueVisitor<Boolean, TypeMirror> VALUE_VALIDATING_VISITOR = @@ -216,17 +225,8 @@ public final class SuperficialValidation { if (!expectedType.getKind().equals(TypeKind.ARRAY)) { return false; } - try { - expectedType = MoreTypes.asArray(expectedType).getComponentType(); - } catch (IllegalArgumentException e) { - return false; // Not an array expected, ergo invalid. - } - for (AnnotationValue value : values) { - if (!value.accept(this, expectedType)) { - return false; - } - } - return true; + TypeMirror componentType = MoreTypes.asArray(expectedType).getComponentType(); + return values.stream().allMatch(value -> value.accept(this, componentType)); } @Override @@ -280,4 +280,6 @@ public final class SuperficialValidation { AnnotationValue annotationValue, TypeMirror expectedType) { return annotationValue.accept(VALUE_VALIDATING_VISITOR, expectedType); } + + private SuperficialValidation() {} } diff --git a/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java b/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java index 59a135c4..f7534503 100644 --- a/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java +++ b/common/src/test/java/com/google/auto/common/BasicAnnotationProcessorTest.java @@ -18,28 +18,35 @@ package com.google.auto.common; import static com.google.common.collect.Multimaps.transformValues; import static com.google.common.truth.Truth.assertAbout; import static com.google.common.truth.Truth.assertThat; +import static com.google.testing.compile.CompilationSubject.assertThat; +import static com.google.testing.compile.Compiler.javac; import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource; import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources; import static javax.tools.Diagnostic.Kind.ERROR; import static javax.tools.StandardLocation.SOURCE_OUTPUT; +import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep; +import com.google.auto.common.BasicAnnotationProcessor.Step; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.SetMultimap; import com.google.common.truth.Correspondence; +import com.google.testing.compile.Compilation; +import com.google.testing.compile.CompilationRule; import com.google.testing.compile.JavaFileObjects; import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Set; import javax.annotation.processing.Filer; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; import javax.tools.JavaFileObject; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -47,30 +54,36 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class BasicAnnotationProcessorTest { + private abstract static class BaseAnnotationProcessor extends BasicAnnotationProcessor { + + static final String ENCLOSING_CLASS_NAME = + BasicAnnotationProcessorTest.class.getCanonicalName(); + + @Override + public final SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + } + @Retention(RetentionPolicy.SOURCE) public @interface RequiresGeneratedCode {} /** * Rejects elements unless the class generated by {@link GeneratesCode}'s processor is present. */ - private static final class RequiresGeneratedCodeProcessor extends BasicAnnotationProcessor { + private static class RequiresGeneratedCodeProcessor extends BaseAnnotationProcessor { int rejectedRounds; - final ImmutableList.Builder<ImmutableSetMultimap<Class<? extends Annotation>, Element>> - processArguments = ImmutableList.builder(); + final ImmutableList.Builder<ImmutableSetMultimap<String, Element>> processArguments = + ImmutableList.builder(); @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latestSupported(); - } - - @Override - protected Iterable<? extends ProcessingStep> initSteps() { + protected Iterable<? extends Step> steps() { return ImmutableSet.of( - new ProcessingStep() { + new Step() { @Override - public Set<Element> process( - SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) { + public ImmutableSet<? extends Element> process( + ImmutableSetMultimap<String, Element> elementsByAnnotation) { processArguments.add(ImmutableSetMultimap.copyOf(elementsByAnnotation)); TypeElement requiredClass = processingEnv.getElementUtils().getTypeElement("test.SomeGeneratedClass"); @@ -83,25 +96,25 @@ public class BasicAnnotationProcessorTest { } @Override - public Set<? extends Class<? extends Annotation>> annotations() { - return ImmutableSet.of(RequiresGeneratedCode.class); + public ImmutableSet<String> annotations() { + return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".RequiresGeneratedCode"); } }, - new ProcessingStep() { + new Step() { @Override - public Set<? extends Class<? extends Annotation>> annotations() { - return ImmutableSet.of(AnAnnotation.class); + public ImmutableSet<? extends Element> process( + ImmutableSetMultimap<String, Element> elementsByAnnotation) { + return ImmutableSet.of(); } @Override - public Set<? extends Element> process( - SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) { - return ImmutableSet.of(); + public ImmutableSet<String> annotations() { + return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".AnAnnotation"); } }); } - ImmutableList<ImmutableSetMultimap<Class<? extends Annotation>, Element>> processArguments() { + ImmutableList<ImmutableSetMultimap<String, Element>> processArguments() { return processArguments.build(); } } @@ -110,27 +123,21 @@ public class BasicAnnotationProcessorTest { public @interface GeneratesCode {} /** Generates a class called {@code test.SomeGeneratedClass}. */ - public class GeneratesCodeProcessor extends BasicAnnotationProcessor { - + public static class GeneratesCodeProcessor extends BaseAnnotationProcessor { @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latestSupported(); - } - - @Override - protected Iterable<? extends ProcessingStep> initSteps() { + protected Iterable<? extends Step> steps() { return ImmutableSet.of( - new ProcessingStep() { + new Step() { @Override - public Set<Element> process( - SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) { + public ImmutableSet<? extends Element> process( + ImmutableSetMultimap<String, Element> elementsByAnnotation) { generateClass(processingEnv.getFiler(), "SomeGeneratedClass"); return ImmutableSet.of(); } @Override - public Set<? extends Class<? extends Annotation>> annotations() { - return ImmutableSet.of(GeneratesCode.class); + public ImmutableSet<String> annotations() { + return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".GeneratesCode"); } }); } @@ -139,20 +146,15 @@ public class BasicAnnotationProcessorTest { public @interface AnAnnotation {} /** When annotating a type {@code Foo}, generates a class called {@code FooXYZ}. */ - public class AnAnnotationProcessor extends BasicAnnotationProcessor { + public static class AnAnnotationProcessor extends BaseAnnotationProcessor { @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latestSupported(); - } - - @Override - protected Iterable<? extends ProcessingStep> initSteps() { + protected Iterable<? extends Step> steps() { return ImmutableSet.of( - new ProcessingStep() { + new Step() { @Override - public Set<Element> process( - SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) { + public ImmutableSet<Element> process( + ImmutableSetMultimap<String, Element> elementsByAnnotation) { for (Element element : elementsByAnnotation.values()) { generateClass(processingEnv.getFiler(), element.getSimpleName() + "XYZ"); } @@ -160,8 +162,8 @@ public class BasicAnnotationProcessorTest { } @Override - public Set<? extends Class<? extends Annotation>> annotations() { - return ImmutableSet.of(AnAnnotation.class); + public ImmutableSet<String> annotations() { + return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".AnAnnotation"); } }); } @@ -171,19 +173,15 @@ public class BasicAnnotationProcessorTest { public @interface CauseError {} /** Report an error for any class annotated. */ - public static class CauseErrorProcessor extends BasicAnnotationProcessor { - @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latestSupported(); - } + public static class CauseErrorProcessor extends BaseAnnotationProcessor { @Override - protected Iterable<? extends ProcessingStep> initSteps() { + protected Iterable<? extends Step> steps() { return ImmutableSet.of( - new ProcessingStep() { + new Step() { @Override - public Set<Element> process( - SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) { + public ImmutableSet<Element> process( + ImmutableSetMultimap<String, Element> elementsByAnnotation) { for (Element e : elementsByAnnotation.values()) { processingEnv.getMessager().printMessage(ERROR, "purposeful error", e); } @@ -191,26 +189,91 @@ public class BasicAnnotationProcessorTest { } @Override - public Set<? extends Class<? extends Annotation>> annotations() { - return ImmutableSet.of(CauseError.class); + public ImmutableSet<String> annotations() { + return ImmutableSet.of(ENCLOSING_CLASS_NAME + ".CauseError"); + } + }); + } + } + + public static class MissingAnnotationProcessor extends BaseAnnotationProcessor { + + private ImmutableSetMultimap<String, Element> elementsByAnnotation; + + @Override + protected Iterable<? extends Step> steps() { + return ImmutableSet.of( + new Step() { + @Override + public ImmutableSet<Element> process( + ImmutableSetMultimap<String, Element> elementsByAnnotation) { + MissingAnnotationProcessor.this.elementsByAnnotation = elementsByAnnotation; + for (Element element : elementsByAnnotation.values()) { + generateClass(processingEnv.getFiler(), element.getSimpleName() + "XYZ"); + } + return ImmutableSet.of(); + } + + @Override + public ImmutableSet<String> annotations() { + return ImmutableSet.of( + "test.SomeNonExistentClass", ENCLOSING_CLASS_NAME + ".AnAnnotation"); } }); } + + ImmutableSetMultimap<String, Element> getElementsByAnnotation() { + return elementsByAnnotation; + } } - @Test public void properlyDefersProcessing_typeElement() { - JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA", - "package test;", - "", - "@" + RequiresGeneratedCode.class.getCanonicalName(), - "public class ClassA {", - " SomeGeneratedClass sgc;", - "}"); - JavaFileObject classBFileObject = JavaFileObjects.forSourceLines("test.ClassB", - "package test;", - "", - "@" + GeneratesCode.class.getCanonicalName(), - "public class ClassB {}"); + @SuppressWarnings("deprecation") // Deprecated ProcessingStep is being explicitly tested. + static final class MultiAnnotationProcessingStep implements ProcessingStep { + + private SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation; + + @Override + public ImmutableSet<? extends Class<? extends Annotation>> annotations() { + return ImmutableSet.of(AnAnnotation.class, ReferencesAClass.class); + } + + @Override + public ImmutableSet<? extends Element> process( + SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) { + this.elementsByAnnotation = elementsByAnnotation; + return ImmutableSet.of(); + } + + SetMultimap<Class<? extends Annotation>, Element> getElementsByAnnotation() { + return elementsByAnnotation; + } + } + + @Retention(RetentionPolicy.SOURCE) + public @interface ReferencesAClass { + Class<?> value(); + } + + @Rule public CompilationRule compilation = new CompilationRule(); + + @Test + public void properlyDefersProcessing_typeElement() { + JavaFileObject classAFileObject = + JavaFileObjects.forSourceLines( + "test.ClassA", + "package test;", + "", + "@" + RequiresGeneratedCode.class.getCanonicalName(), + "public class ClassA {", + " SomeGeneratedClass sgc;", + "}"); + JavaFileObject classBFileObject = + JavaFileObjects.forSourceLines( + "test.ClassB", + "package test;", + "", + "@" + GeneratesCode.class.getCanonicalName(), + "public class ClassB {}"); RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor = new RequiresGeneratedCodeProcessor(); assertAbout(javaSources()) @@ -244,22 +307,22 @@ public class BasicAnnotationProcessorTest { .generatesFileNamed(SOURCE_OUTPUT, "test", "ValidInRound2XYZ.java"); } - @Retention(RetentionPolicy.SOURCE) - public @interface ReferencesAClass { - Class<?> value(); - } - - @Test public void properlyDefersProcessing_packageElement() { - JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA", - "package test;", - "", - "@" + GeneratesCode.class.getCanonicalName(), - "public class ClassA {", - "}"); - JavaFileObject packageFileObject = JavaFileObjects.forSourceLines("test.package-info", - "@" + RequiresGeneratedCode.class.getCanonicalName(), - "@" + ReferencesAClass.class.getCanonicalName() + "(SomeGeneratedClass.class)", - "package test;"); + @Test + public void properlyDefersProcessing_packageElement() { + JavaFileObject classAFileObject = + JavaFileObjects.forSourceLines( + "test.ClassA", + "package test;", + "", + "@" + GeneratesCode.class.getCanonicalName(), + "public class ClassA {", + "}"); + JavaFileObject packageFileObject = + JavaFileObjects.forSourceLines( + "test.package-info", + "@" + RequiresGeneratedCode.class.getCanonicalName(), + "@" + ReferencesAClass.class.getCanonicalName() + "(SomeGeneratedClass.class)", + "package test;"); RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor = new RequiresGeneratedCodeProcessor(); assertAbout(javaSources()) @@ -272,21 +335,28 @@ public class BasicAnnotationProcessorTest { assertThat(requiresGeneratedCodeProcessor.rejectedRounds).isEqualTo(0); } - @Test public void properlyDefersProcessing_argumentElement() { - JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA", - "package test;", - "", - "public class ClassA {", - " SomeGeneratedClass sgc;", - " public void myMethod(@" + RequiresGeneratedCode.class.getCanonicalName() + " int myInt)", - " {}", - "}"); - JavaFileObject classBFileObject = JavaFileObjects.forSourceLines("test.ClassB", - "package test;", - "", - "public class ClassB {", - " public void myMethod(@" + GeneratesCode.class.getCanonicalName() + " int myInt) {}", - "}"); + @Test + public void properlyDefersProcessing_argumentElement() { + JavaFileObject classAFileObject = + JavaFileObjects.forSourceLines( + "test.ClassA", + "package test;", + "", + "public class ClassA {", + " SomeGeneratedClass sgc;", + " public void myMethod(@" + + RequiresGeneratedCode.class.getCanonicalName() + + " int myInt)", + " {}", + "}"); + JavaFileObject classBFileObject = + JavaFileObjects.forSourceLines( + "test.ClassB", + "package test;", + "", + "public class ClassB {", + " public void myMethod(@" + GeneratesCode.class.getCanonicalName() + " int myInt) {}", + "}"); RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor = new RequiresGeneratedCodeProcessor(); assertAbout(javaSources()) @@ -311,11 +381,13 @@ public class BasicAnnotationProcessorTest { " @" + AnAnnotation.class.getCanonicalName(), " public void method() {}", "}"); - JavaFileObject classBFileObject = JavaFileObjects.forSourceLines("test.ClassB", - "package test;", - "", - "@" + GeneratesCode.class.getCanonicalName(), - "public class ClassB {}"); + JavaFileObject classBFileObject = + JavaFileObjects.forSourceLines( + "test.ClassB", + "package test;", + "", + "@" + GeneratesCode.class.getCanonicalName(), + "public class ClassB {}"); RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor = new RequiresGeneratedCodeProcessor(); assertAbout(javaSources()) @@ -332,8 +404,8 @@ public class BasicAnnotationProcessorTest { assertThat(requiresGeneratedCodeProcessor.processArguments()) .comparingElementsUsing(setMultimapValuesByString()) .containsExactly( - ImmutableSetMultimap.of(RequiresGeneratedCode.class, "test.ClassA"), - ImmutableSetMultimap.of(RequiresGeneratedCode.class, "test.ClassA")) + ImmutableSetMultimap.of(RequiresGeneratedCode.class.getCanonicalName(), "test.ClassA"), + ImmutableSetMultimap.of(RequiresGeneratedCode.class.getCanonicalName(), "test.ClassA")) .inOrder(); } @@ -345,20 +417,60 @@ public class BasicAnnotationProcessorTest { "is equivalent comparing multimap values by `toString()` to"); } - @Test public void reportsMissingType() { - JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA", - "package test;", - "", - "@" + RequiresGeneratedCode.class.getCanonicalName(), - "public class ClassA {", - " SomeGeneratedClass bar;", - "}"); + @Test + public void properlySkipsMissingAnnotations_generatesClass() { + JavaFileObject source = + JavaFileObjects.forSourceLines( + "test.ValidInRound2", + "package test;", + "", + "@" + AnAnnotation.class.getCanonicalName(), + "public class ValidInRound2 {", + " ValidInRound1XYZ vir1xyz;", + " @" + AnAnnotation.class.getCanonicalName(), + " static class ValidInRound1 {}", + "}"); + Compilation compilation = + javac().withProcessors(new MissingAnnotationProcessor()).compile(source); + assertThat(compilation).succeeded(); + assertThat(compilation).generatedSourceFile("test.ValidInRound2XYZ"); + } + + @Test + public void properlySkipsMissingAnnotations_passesValidAnnotationsToProcess() { + JavaFileObject source = + JavaFileObjects.forSourceLines( + "test.ClassA", + "package test;", + "", + "@" + AnAnnotation.class.getCanonicalName(), + "public class ClassA {", + "}"); + MissingAnnotationProcessor missingAnnotationProcessor = new MissingAnnotationProcessor(); + assertThat(javac().withProcessors(missingAnnotationProcessor).compile(source)).succeeded(); + assertThat(missingAnnotationProcessor.getElementsByAnnotation().keySet()) + .containsExactly(AnAnnotation.class.getCanonicalName()); + assertThat(missingAnnotationProcessor.getElementsByAnnotation().values()).hasSize(1); + } + + @Test + public void reportsMissingType() { + JavaFileObject classAFileObject = + JavaFileObjects.forSourceLines( + "test.ClassA", + "package test;", + "", + "@" + RequiresGeneratedCode.class.getCanonicalName(), + "public class ClassA {", + " SomeGeneratedClass bar;", + "}"); assertAbout(javaSources()) .that(ImmutableList.of(classAFileObject)) .processedWith(new RequiresGeneratedCodeProcessor()) .failsToCompile() .withErrorContaining(RequiresGeneratedCodeProcessor.class.getCanonicalName()) - .in(classAFileObject).onLine(4); + .in(classAFileObject) + .onLine(4); } @Test @@ -378,6 +490,45 @@ public class BasicAnnotationProcessorTest { .withErrorContaining("purposeful"); } + @Test + public void processingStepAsStepAnnotationsNamesMatchClasses() { + Step step = BasicAnnotationProcessor.asStep(new MultiAnnotationProcessingStep()); + + assertThat(step.annotations()) + .containsExactly( + AnAnnotation.class.getCanonicalName(), ReferencesAClass.class.getCanonicalName()); + } + + /** + * Tests that a {@link ProcessingStep} passed to {@link + * BasicAnnotationProcessor#asStep(ProcessingStep)} still gets passed the correct arguments to + * {@link Step#process(ImmutableSetMultimap)}. + */ + @Test + public void processingStepAsStepProcessElementsMatchClasses() { + Elements elements = compilation.getElements(); + String anAnnotationName = AnAnnotation.class.getCanonicalName(); + String referencesAClassName = ReferencesAClass.class.getCanonicalName(); + TypeElement anAnnotationElement = elements.getTypeElement(anAnnotationName); + TypeElement referencesAClassElement = elements.getTypeElement(referencesAClassName); + MultiAnnotationProcessingStep processingStep = new MultiAnnotationProcessingStep(); + + BasicAnnotationProcessor.asStep(processingStep) + .process( + ImmutableSetMultimap.of( + anAnnotationName, + anAnnotationElement, + referencesAClassName, + referencesAClassElement)); + + assertThat(processingStep.getElementsByAnnotation()) + .containsExactly( + AnAnnotation.class, + anAnnotationElement, + ReferencesAClass.class, + referencesAClassElement); + } + private static void generateClass(Filer filer, String generatedClassName) { PrintWriter writer = null; try { diff --git a/factory/Android.bp b/factory/Android.bp index bf4899bf..7e75542d 100644 --- a/factory/Android.bp +++ b/factory/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "external_auto_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["external_auto_license"], +} + java_library { name: "auto_factory_annotations", host_supported: true, diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/DaggerModule.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/DaggerModule.java index be9478fc..4c4a38af 100644 --- a/factory/src/it/functional/src/main/java/com/google/auto/factory/DaggerModule.java +++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/DaggerModule.java @@ -15,6 +15,7 @@ */ package com.google.auto.factory; +import com.google.auto.factory.otherpackage.OtherPackage; import dagger.Module; import dagger.Provides; @@ -45,4 +46,14 @@ final class DaggerModule { Number provideNumber() { return 3; } + + @Provides + ReferencePackage provideReferencePackage(ReferencePackageFactory factory) { + return factory.create(17); + } + + @Provides + OtherPackage provideOtherPackage() { + return new OtherPackage(null, 23); + } } diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/FactoryComponent.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/FactoryComponent.java index 7d0a1627..b5bbe3d9 100644 --- a/factory/src/it/functional/src/main/java/com/google/auto/factory/FactoryComponent.java +++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/FactoryComponent.java @@ -15,6 +15,7 @@ */ package com.google.auto.factory; +import com.google.auto.factory.otherpackage.OtherPackageFactory; import dagger.Component; /** A component to materialize the factory using Dagger 2 */ @@ -23,4 +24,8 @@ interface FactoryComponent { FooFactory factory(); GenericFooFactory<Number> generatedFactory(); + + ReferencePackageFactory referencePackageFactory(); + + OtherPackageFactory otherPackageFactory(); } diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/ReferencePackage.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/ReferencePackage.java new file mode 100644 index 00000000..22aff651 --- /dev/null +++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/ReferencePackage.java @@ -0,0 +1,38 @@ +/* + * Copyright 2020 Google LLC + * + * 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 com.google.auto.factory; + +import com.google.auto.factory.otherpackage.OtherPackage; +import com.google.auto.factory.otherpackage.OtherPackageFactory; +import javax.inject.Inject; + +@AutoFactory +public class ReferencePackage { + private final OtherPackageFactory otherPackageFactory; + private final int random; + + @Inject + public ReferencePackage( + @Provided OtherPackageFactory otherPackageFactory, + int random) { + this.otherPackageFactory = otherPackageFactory; + this.random = random; + } + + public OtherPackage otherPackage() { + return otherPackageFactory.create(random); + } +} diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/otherpackage/OtherPackage.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/otherpackage/OtherPackage.java new file mode 100644 index 00000000..b9925a18 --- /dev/null +++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/otherpackage/OtherPackage.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Google LLC + * + * 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 com.google.auto.factory.otherpackage; + +import com.google.auto.factory.AutoFactory; +import com.google.auto.factory.Provided; +import com.google.auto.factory.ReferencePackageFactory; + +@AutoFactory +public class OtherPackage { + private final ReferencePackageFactory referencePackageFactory; + private final int random; + + public OtherPackage(@Provided ReferencePackageFactory referencePackageFactory, int random) { + this.referencePackageFactory = referencePackageFactory; + this.random = random; + } + + public ReferencePackageFactory referencePackageFactory() { + return referencePackageFactory; + } + + public int random() { + return random; + } +} diff --git a/factory/src/it/functional/src/test/java/com/google/auto/factory/DependencyInjectionIntegrationTest.java b/factory/src/it/functional/src/test/java/com/google/auto/factory/DependencyInjectionIntegrationTest.java index ebd83367..e1412117 100644 --- a/factory/src/it/functional/src/test/java/com/google/auto/factory/DependencyInjectionIntegrationTest.java +++ b/factory/src/it/functional/src/test/java/com/google/auto/factory/DependencyInjectionIntegrationTest.java @@ -4,6 +4,7 @@ import static com.google.common.truth.Truth.assertThat; import com.google.auto.factory.GenericFoo.DepE; import com.google.auto.factory.GenericFoo.IntAndStringAccessor; +import com.google.auto.factory.otherpackage.OtherPackage; import com.google.common.collect.ImmutableList; import com.google.inject.Guice; import com.google.inject.Key; @@ -64,6 +65,16 @@ public class DependencyInjectionIntegrationTest { assertThat(four.getDepE()).isEqualTo(DepE.VALUE_2); } + @Test + public void daggerInjectedPackageSpanningFactory() { + FactoryComponent component = DaggerFactoryComponent.create(); + ReferencePackageFactory referencePackageFactory = component.referencePackageFactory(); + ReferencePackage referencePackage = referencePackageFactory.create(5); + OtherPackage otherPackage = referencePackage.otherPackage(); + assertThat(otherPackage.referencePackageFactory()).isNotSameInstanceAs(referencePackageFactory); + assertThat(otherPackage.random()).isEqualTo(5); + } + @Test public void guiceInjectedFactory() { FooFactory fooFactory = Guice.createInjector(new GuiceModule()).getInstance(FooFactory.class); Foo one = fooFactory.create("A"); @@ -109,4 +120,15 @@ public class DependencyInjectionIntegrationTest { assertThat(four.passThrough(5L)).isEqualTo(5L); assertThat(four.getDepE()).isEqualTo(DepE.VALUE_2); } + + @Test + public void guiceInjectedPackageSpanningFactory() { + ReferencePackageFactory referencePackageFactory = + Guice.createInjector(new GuiceModule()) + .getInstance(ReferencePackageFactory.class); + ReferencePackage referencePackage = referencePackageFactory.create(5); + OtherPackage otherPackage = referencePackage.otherPackage(); + assertThat(otherPackage.referencePackageFactory()).isNotSameInstanceAs(referencePackageFactory); + assertThat(otherPackage.random()).isEqualTo(5); + } } diff --git a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryDeclaration.java b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryDeclaration.java index ad4ccb8b..e2da6eae 100644 --- a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryDeclaration.java +++ b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryDeclaration.java @@ -62,21 +62,17 @@ abstract class AutoFactoryDeclaration { abstract AnnotationMirror mirror(); abstract ImmutableMap<String, AnnotationValue> valuesMap(); - String getFactoryName() { - CharSequence packageName = getPackage(targetType()).getQualifiedName(); - StringBuilder builder = new StringBuilder(packageName); - if (packageName.length() > 0) { - builder.append('.'); - } + PackageAndClass getFactoryName() { + String packageName = getPackage(targetType()).getQualifiedName().toString(); if (className().isPresent()) { - builder.append(className().get()); - } else { - for (String enclosingSimpleName : targetEnclosingSimpleNames()) { - builder.append(enclosingSimpleName).append('_'); - } - builder.append(targetType().getSimpleName()).append("Factory"); + return PackageAndClass.of(packageName, className().get()); + } + StringBuilder builder = new StringBuilder(); + for (String enclosingSimpleName : targetEnclosingSimpleNames()) { + builder.append(enclosingSimpleName).append('_'); } - return builder.toString(); + builder.append(targetType().getSimpleName()).append("Factory"); + return PackageAndClass.of(packageName, builder.toString()); } private ImmutableList<String> targetEnclosingSimpleNames() { diff --git a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java index cf3d5ebd..5cc1d94d 100644 --- a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java +++ b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java @@ -27,13 +27,10 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; -import com.google.googlejavaformat.java.filer.FormattingFiler; import java.io.IOException; import java.util.Arrays; -import java.util.Collection; import java.util.Comparator; import java.util.List; -import java.util.Map.Entry; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Messager; @@ -68,7 +65,6 @@ public final class AutoFactoryProcessor extends AbstractProcessor { private Messager messager; private Elements elements; private Types types; - private FactoryWriter factoryWriter; @Override public synchronized void init(ProcessingEnvironment processingEnv) { @@ -76,11 +72,6 @@ public final class AutoFactoryProcessor extends AbstractProcessor { elements = processingEnv.getElementUtils(); types = processingEnv.getTypeUtils(); messager = processingEnv.getMessager(); - factoryWriter = - new FactoryWriter( - new FormattingFiler(processingEnv.getFiler()), - elements, - processingEnv.getSourceVersion()); providedChecker = new ProvidedChecker(messager); declarationFactory = new AutoFactoryDeclaration.Factory(elements, messager); factoryDescriptorGenerator = @@ -103,14 +94,15 @@ public final class AutoFactoryProcessor extends AbstractProcessor { providedChecker.checkProvidedParameter(element); } - ImmutableListMultimap.Builder<String, FactoryMethodDescriptor> indexedMethods = + ImmutableListMultimap.Builder<PackageAndClass, FactoryMethodDescriptor> indexedMethodsBuilder = ImmutableListMultimap.builder(); - ImmutableSetMultimap.Builder<String, ImplementationMethodDescriptor> + ImmutableSetMultimap.Builder<PackageAndClass, ImplementationMethodDescriptor> implementationMethodDescriptorsBuilder = ImmutableSetMultimap.builder(); + // Iterate over the classes and methods that are annotated with @AutoFactory. for (Element element : roundEnv.getElementsAnnotatedWith(AutoFactory.class)) { Optional<AutoFactoryDeclaration> declaration = declarationFactory.createIfValid(element); if (declaration.isPresent()) { - String factoryName = declaration.get().getFactoryName(); + PackageAndClass factoryName = declaration.get().getFactoryName(); TypeElement extendingType = declaration.get().extendingType(); implementationMethodDescriptorsBuilder.putAll( factoryName, implementationMethods(extendingType, element)); @@ -123,62 +115,61 @@ public final class AutoFactoryProcessor extends AbstractProcessor { ImmutableSet<FactoryMethodDescriptor> descriptors = factoryDescriptorGenerator.generateDescriptor(element); for (FactoryMethodDescriptor descriptor : descriptors) { - indexedMethods.put(descriptor.factoryName(), descriptor); + indexedMethodsBuilder.put(descriptor.factoryName(), descriptor); } } - ImmutableSetMultimap<String, ImplementationMethodDescriptor> + ImmutableSetMultimap<PackageAndClass, ImplementationMethodDescriptor> implementationMethodDescriptors = implementationMethodDescriptorsBuilder.build(); + ImmutableListMultimap<PackageAndClass, FactoryMethodDescriptor> indexedMethods = + indexedMethodsBuilder.build(); + ImmutableSetMultimap<String, PackageAndClass> factoriesBeingCreated = + simpleNamesToNames(indexedMethods.keySet()); + FactoryWriter factoryWriter = new FactoryWriter(processingEnv, factoriesBeingCreated); - for (Entry<String, Collection<FactoryMethodDescriptor>> entry - : indexedMethods.build().asMap().entrySet()) { - ImmutableSet.Builder<TypeMirror> extending = ImmutableSet.builder(); - ImmutableSortedSet.Builder<TypeMirror> implementing = - ImmutableSortedSet.orderedBy( - new Comparator<TypeMirror>() { - @Override - public int compare(TypeMirror first, TypeMirror second) { - String firstName = MoreTypes.asTypeElement(first).getQualifiedName().toString(); - String secondName = MoreTypes.asTypeElement(second).getQualifiedName().toString(); - return firstName.compareTo(secondName); - } - }); - boolean publicType = false; - Boolean allowSubclasses = null; - boolean skipCreation = false; - for (FactoryMethodDescriptor methodDescriptor : entry.getValue()) { - extending.add(methodDescriptor.declaration().extendingType().asType()); - for (TypeElement implementingType : methodDescriptor.declaration().implementingTypes()) { - implementing.add(implementingType.asType()); - } - publicType |= methodDescriptor.publicMethod(); - if (allowSubclasses == null) { - allowSubclasses = methodDescriptor.declaration().allowSubclasses(); - } else if (!allowSubclasses.equals(methodDescriptor.declaration().allowSubclasses())) { - skipCreation = true; - messager.printMessage(Kind.ERROR, - "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.", - methodDescriptor.declaration().target(), - methodDescriptor.declaration().mirror(), - methodDescriptor.declaration().valuesMap().get("allowSubclasses")); - } - } - if (!skipCreation) { - try { - factoryWriter.writeFactory( - FactoryDescriptor.create( - entry.getKey(), - Iterables.getOnlyElement(extending.build()), - implementing.build(), - publicType, - ImmutableSet.copyOf(entry.getValue()), - implementationMethodDescriptors.get(entry.getKey()), - allowSubclasses)); - } catch (IOException e) { - messager.printMessage(Kind.ERROR, "failed: " + e); - } - } - } + indexedMethods.asMap().forEach( + (factoryName, methodDescriptors) -> { + // The sets of classes that are mentioned in the `extending` and `implementing` elements, + // respectively, of the @AutoFactory annotations for this factory. + ImmutableSet.Builder<TypeMirror> extending = newTypeSetBuilder(); + ImmutableSortedSet.Builder<TypeMirror> implementing = newTypeSetBuilder(); + boolean publicType = false; + Boolean allowSubclasses = null; + boolean skipCreation = false; + for (FactoryMethodDescriptor methodDescriptor : methodDescriptors) { + extending.add(methodDescriptor.declaration().extendingType().asType()); + for (TypeElement implementingType : + methodDescriptor.declaration().implementingTypes()) { + implementing.add(implementingType.asType()); + } + publicType |= methodDescriptor.publicMethod(); + if (allowSubclasses == null) { + allowSubclasses = methodDescriptor.declaration().allowSubclasses(); + } else if (!allowSubclasses.equals(methodDescriptor.declaration().allowSubclasses())) { + skipCreation = true; + messager.printMessage(Kind.ERROR, + "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.", + methodDescriptor.declaration().target(), + methodDescriptor.declaration().mirror(), + methodDescriptor.declaration().valuesMap().get("allowSubclasses")); + } + } + if (!skipCreation) { + try { + factoryWriter.writeFactory( + FactoryDescriptor.create( + factoryName, + Iterables.getOnlyElement(extending.build()), + implementing.build(), + publicType, + ImmutableSet.copyOf(methodDescriptors), + implementationMethodDescriptors.get(factoryName), + allowSubclasses)); + } catch (IOException e) { + messager.printMessage(Kind.ERROR, "failed: " + e); + } + } + }); } private ImmutableSet<ImplementationMethodDescriptor> implementationMethods( @@ -216,8 +207,24 @@ public final class AutoFactoryProcessor extends AbstractProcessor { return Iterables.getOnlyElement(types).asType(); } + private static ImmutableSetMultimap<String, PackageAndClass> simpleNamesToNames( + ImmutableSet<PackageAndClass> names) { + // .collect(toImmutableSetMultimap(...)) would make this much simpler but ran into problems in + // Google's internal build system because of multiple Guava versions. + ImmutableSetMultimap.Builder<String, PackageAndClass> builder = ImmutableSetMultimap.builder(); + for (PackageAndClass name : names) { + builder.put(name.className(), name); + } + return builder.build(); + } + + private static ImmutableSortedSet.Builder<TypeMirror> newTypeSetBuilder() { + return ImmutableSortedSet.orderedBy( + Comparator.comparing(t -> MoreTypes.asTypeElement(t).getQualifiedName().toString())); + } + @Override - public Set<String> getSupportedAnnotationTypes() { + public ImmutableSet<String> getSupportedAnnotationTypes() { return ImmutableSet.of(AutoFactory.class.getName(), Provided.class.getName()); } diff --git a/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptor.java b/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptor.java index 68ae678d..5ed2307a 100644 --- a/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptor.java +++ b/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptor.java @@ -46,7 +46,7 @@ abstract class FactoryDescriptor { } }; - abstract String name(); + abstract PackageAndClass name(); abstract TypeMirror extendingType(); abstract ImmutableSet<TypeMirror> implementingTypes(); abstract boolean publicType(); @@ -76,7 +76,7 @@ abstract class FactoryDescriptor { } static FactoryDescriptor create( - String name, + PackageAndClass name, TypeMirror extendingType, ImmutableSet<TypeMirror> implementingTypes, boolean publicType, diff --git a/factory/src/main/java/com/google/auto/factory/processor/FactoryMethodDescriptor.java b/factory/src/main/java/com/google/auto/factory/processor/FactoryMethodDescriptor.java index c3a0159d..43e5097d 100644 --- a/factory/src/main/java/com/google/auto/factory/processor/FactoryMethodDescriptor.java +++ b/factory/src/main/java/com/google/auto/factory/processor/FactoryMethodDescriptor.java @@ -41,7 +41,7 @@ abstract class FactoryMethodDescriptor { abstract Builder toBuilder(); abstract boolean isVarArgs(); - final String factoryName() { + final PackageAndClass factoryName() { return declaration().getFactoryName(); } @@ -54,7 +54,7 @@ abstract class FactoryMethodDescriptor { } @AutoValue.Builder - static abstract class Builder { + abstract static class Builder { abstract Builder declaration(AutoFactoryDeclaration declaration); abstract Builder name(String name); abstract Builder returnType(TypeMirror returnType); diff --git a/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java b/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java index 3bb68e1c..53b99cb5 100644 --- a/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java +++ b/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java @@ -29,6 +29,7 @@ import com.google.common.base.Joiner; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.squareup.javapoet.AnnotationSpec; @@ -44,10 +45,12 @@ import com.squareup.javapoet.TypeVariableName; import java.io.IOException; import java.util.Iterator; import javax.annotation.processing.Filer; +import javax.annotation.processing.ProcessingEnvironment; import javax.inject.Inject; import javax.inject.Provider; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.util.Elements; @@ -57,18 +60,22 @@ final class FactoryWriter { private final Filer filer; private final Elements elements; private final SourceVersion sourceVersion; + private final ImmutableSetMultimap<String, PackageAndClass> factoriesBeingCreated; - FactoryWriter(Filer filer, Elements elements, SourceVersion sourceVersion) { - this.filer = filer; - this.elements = elements; - this.sourceVersion = sourceVersion; + FactoryWriter( + ProcessingEnvironment processingEnv, + ImmutableSetMultimap<String, PackageAndClass> factoriesBeingCreated) { + this.filer = processingEnv.getFiler(); + this.elements = processingEnv.getElementUtils(); + this.sourceVersion = processingEnv.getSourceVersion(); + this.factoriesBeingCreated = factoriesBeingCreated; } private static final Joiner ARGUMENT_JOINER = Joiner.on(", "); - void writeFactory(final FactoryDescriptor descriptor) + void writeFactory(FactoryDescriptor descriptor) throws IOException { - String factoryName = getSimpleName(descriptor.name()).toString(); + String factoryName = descriptor.name().className(); TypeSpec.Builder factory = classBuilder(factoryName) .addOriginatingElement(descriptor.declaration().targetType()); @@ -98,7 +105,7 @@ final class FactoryWriter { addImplementationMethods(factory, descriptor); addCheckNotNullMethod(factory, descriptor); - JavaFile.builder(getPackage(descriptor.name()), factory.build()) + JavaFile.builder(descriptor.name().packageName(), factory.build()) .skipJavaLangImports(true) .build() .writeTo(filer); @@ -109,7 +116,7 @@ final class FactoryWriter { factory.addTypeVariables(typeVariableNames); } - private static void addConstructorAndProviderFields( + private void addConstructorAndProviderFields( TypeSpec.Builder factory, FactoryDescriptor descriptor) { MethodSpec.Builder constructor = constructorBuilder().addAnnotation(Inject.class); if (descriptor.publicType()) { @@ -118,7 +125,7 @@ final class FactoryWriter { Iterator<ProviderField> providerFields = descriptor.providers().values().iterator(); for (int argumentIndex = 1; providerFields.hasNext(); argumentIndex++) { ProviderField provider = providerFields.next(); - TypeName typeName = TypeName.get(provider.key().type().get()).box(); + TypeName typeName = resolveTypeName(provider.key().type().get()).box(); TypeName providerType = ParameterizedTypeName.get(ClassName.get(Provider.class), typeName); factory.addField(providerType, provider.name(), PRIVATE, FINAL); if (provider.key().qualifier().isPresent()) { @@ -132,7 +139,7 @@ final class FactoryWriter { factory.addMethod(constructor.build()); } - private static void addFactoryMethods( + private void addFactoryMethods( TypeSpec.Builder factory, FactoryDescriptor descriptor, ImmutableSet<TypeVariableName> factoryTypeVariables) { @@ -183,7 +190,7 @@ final class FactoryWriter { } } - private static void addImplementationMethods( + private void addImplementationMethods( TypeSpec.Builder factory, FactoryDescriptor descriptor) { for (ImplementationMethodDescriptor methodDescriptor : descriptor.implementationMethodDescriptors()) { @@ -215,11 +222,11 @@ final class FactoryWriter { * {@link ParameterSpec}s to match {@code parameters}. Note that the type of the {@link * ParameterSpec}s match {@link Parameter#type()} and not {@link Key#type()}. */ - private static Iterable<ParameterSpec> parameters(Iterable<Parameter> parameters) { + private ImmutableList<ParameterSpec> parameters(Iterable<Parameter> parameters) { ImmutableList.Builder<ParameterSpec> builder = ImmutableList.builder(); for (Parameter parameter : parameters) { ParameterSpec.Builder parameterBuilder = - ParameterSpec.builder(TypeName.get(parameter.type().get()), parameter.name()); + ParameterSpec.builder(resolveTypeName(parameter.type().get()), parameter.name()); for (AnnotationMirror annotation : Iterables.concat(parameter.nullable().asSet(), parameter.key().qualifier().asSet())) { parameterBuilder.addAnnotation(AnnotationSpec.get(annotation)); @@ -266,23 +273,34 @@ final class FactoryWriter { return false; } - private static CharSequence getSimpleName(CharSequence fullyQualifiedName) { - int lastDot = lastIndexOf(fullyQualifiedName, '.'); - return fullyQualifiedName.subSequence(lastDot + 1, fullyQualifiedName.length()); - } - - private static String getPackage(CharSequence fullyQualifiedName) { - int lastDot = lastIndexOf(fullyQualifiedName, '.'); - return lastDot == -1 ? "" : fullyQualifiedName.subSequence(0, lastDot).toString(); - } - - private static int lastIndexOf(CharSequence charSequence, char c) { - for (int i = charSequence.length() - 1; i >= 0; i--) { - if (charSequence.charAt(i) == c) { - return i; - } + /** + * Returns an appropriate {@code TypeName} for the given type. If the type is an + * {@code ErrorType}, and if it is a simple-name reference to one of the {@code *Factory} + * classes that we are going to generate, then we return its fully-qualified name. In every other + * case we just return {@code TypeName.get(type)}. Specifically, if it is an {@code ErrorType} + * referencing some other type, or referencing one of the classes we are going to generate but + * using its fully-qualified name, then we leave it as-is. JavaPoet treats {@code TypeName.get(t)} + * the same for {@code ErrorType} as for {@code DeclaredType}, which means that if this is a name + * that will eventually be generated then the code we write that references the type will in fact + * compile. + * + * <p>A simpler alternative would be to defer processing to a later round if we find an + * {@code @AutoFactory} class that references undefined types, under the assumption that something + * else will generate those types in the meanwhile. However, this would fail if for example + * {@code @AutoFactory class Foo} has a constructor parameter of type {@code BarFactory} and + * {@code @AutoFactory class Bar} has a constructor parameter of type {@code FooFactory}. We did + * in fact find instances of this in Google's source base. + */ + private TypeName resolveTypeName(TypeMirror type) { + if (type.getKind() != TypeKind.ERROR) { + return TypeName.get(type); + } + ImmutableSet<PackageAndClass> factoryNames = factoriesBeingCreated.get(type.toString()); + if (factoryNames.size() == 1) { + PackageAndClass packageAndClass = Iterables.getOnlyElement(factoryNames); + return ClassName.get(packageAndClass.packageName(), packageAndClass.className()); } - return -1; + return TypeName.get(type); } private static ImmutableSet<TypeVariableName> getFactoryTypeVariables( diff --git a/factory/src/main/java/com/google/auto/factory/processor/PackageAndClass.java b/factory/src/main/java/com/google/auto/factory/processor/PackageAndClass.java new file mode 100644 index 00000000..857fba11 --- /dev/null +++ b/factory/src/main/java/com/google/auto/factory/processor/PackageAndClass.java @@ -0,0 +1,38 @@ +/* + * Copyright 2020 Google LLC + * + * 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 com.google.auto.factory.processor; + +import com.google.auto.value.AutoValue; + +/** A Java class name, separated into its package part and its class part. */ +@AutoValue +abstract class PackageAndClass { + /** + * The package part of this class name. For {@code java.util.Map.Entry}, it would be {@code + * java.util}. + */ + abstract String packageName(); + + /** + * The class part of this class name. For {@code java.util.Map.Entry}, it would be {@code + * Map.Entry}. + */ + abstract String className(); + + static PackageAndClass of(String packageName, String className) { + return new AutoValue_PackageAndClass(packageName, className); + } +} diff --git a/service/Android.bp b/service/Android.bp index 033fd84a..318c47b9 100644 --- a/service/Android.bp +++ b/service/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "external_auto_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["external_auto_license"], +} + java_library { name: "auto_service_annotations", host_supported: true, diff --git a/value/Android.bp b/value/Android.bp index 2e1d819d..73a72154 100644 --- a/value/Android.bp +++ b/value/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "external_auto_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["external_auto_license"], +} + filegroup { name: "auto_value_plugin_resources", path: "src/main/java", @@ -35,7 +44,14 @@ java_library { host_supported: true, srcs: ["src/main/java/com/google/auto/value/*.java"], sdk_version: "core_current", + // AutoValue is a pure java library without any dependency on android framework, however due to + // a dependency from an apex it is required to have a min_sdk_version + min_sdk_version: "19", visibility: ["//visibility:public"], + apex_available: [ + "//apex_available:platform", + "com.android.extservices", + ], } java_plugin { diff --git a/value/annotations/pom.xml b/value/annotations/pom.xml index d0d4a6bb..d6c03de5 100644 --- a/value/annotations/pom.xml +++ b/value/annotations/pom.xml @@ -21,12 +21,12 @@ <parent> <groupId>com.google.auto.value</groupId> <artifactId>auto-value-parent</artifactId> - <version>HEAD-SNAPSHOT</version> + <version>1.7.4</version> </parent> <groupId>com.google.auto.value</groupId> <artifactId>auto-value-annotations</artifactId> - <version>HEAD-SNAPSHOT</version> + <version>1.7.4</version> <name>AutoValue Annotations</name> <description> Immutable value-type code generation for Java 1.6+. diff --git a/value/pom.xml b/value/pom.xml index 9f8be5ac..dcf2cb1d 100644 --- a/value/pom.xml +++ b/value/pom.xml @@ -26,7 +26,7 @@ <groupId>com.google.auto.value</groupId> <artifactId>auto-value-parent</artifactId> - <version>HEAD-SNAPSHOT</version> + <version>1.7.4</version> <name>AutoValue Parent</name> <description> Immutable value-type code generation for Java 7+. diff --git a/value/processor/pom.xml b/value/processor/pom.xml index 0aa9ec8e..5968352c 100644 --- a/value/processor/pom.xml +++ b/value/processor/pom.xml @@ -21,12 +21,12 @@ <parent> <groupId>com.google.auto.value</groupId> <artifactId>auto-value-parent</artifactId> - <version>HEAD-SNAPSHOT</version> + <version>1.7.4</version> </parent> <groupId>com.google.auto.value</groupId> <artifactId>auto-value</artifactId> - <version>HEAD-SNAPSHOT</version> + <version>1.7.4</version> <name>AutoValue Processor</name> <description> Immutable value-type code generation for Java 1.6+. diff --git a/value/src/it/functional/pom.xml b/value/src/it/functional/pom.xml index 4157da96..750b9c43 100644 --- a/value/src/it/functional/pom.xml +++ b/value/src/it/functional/pom.xml @@ -22,14 +22,14 @@ <parent> <groupId>com.google.auto.value</groupId> <artifactId>auto-value-parent</artifactId> - <version>HEAD-SNAPSHOT</version> + <version>1.7.4</version> <relativePath>../../../pom.xml</relativePath> </parent> <url>https://github.com/google/auto/tree/master/value</url> <groupId>com.google.auto.value.it.functional</groupId> <artifactId>functional</artifactId> - <version>HEAD-SNAPSHOT</version> + <version>1.7.4</version> <name>Auto-Value Functional Integration Test</name> <properties> <exclude.tests>this-matches-nothing</exclude.tests> diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueJava8Test.java b/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueJava8Test.java index 27356c52..10812f8d 100644 --- a/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueJava8Test.java +++ b/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueJava8Test.java @@ -19,7 +19,7 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.Truth8.assertThat; import static com.google.testing.compile.CompilationSubject.assertThat; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import static org.junit.Assume.assumeTrue; import com.google.common.collect.ImmutableList; @@ -375,12 +375,9 @@ public class AutoValueJava8Test { @Test public void testNotNullablePrimitiveArrays() { - try { - PrimitiveArrays.create(null, new int[0]); - fail("Construction with null value for non-@Nullable array should have failed"); - } catch (NullPointerException e) { - assertThat(e.getMessage()).contains("booleans"); - } + NullPointerException e = + assertThrows(NullPointerException.class, () -> PrimitiveArrays.create(null, new int[0])); + assertThat(e).hasMessageThat().contains("booleans"); } @AutoValue @@ -421,12 +418,10 @@ public class AutoValueJava8Test { assertThat(instance3.notNullable()).isEqualTo("hello"); assertThat(instance3.nullable()).isEqualTo("world"); - try { - NullablePropertyWithBuilder.builder().build(); - fail("Expected IllegalStateException for unset non-@Nullable property"); - } catch (IllegalStateException e) { - assertThat(e.getMessage()).contains("notNullable"); - } + IllegalStateException e = + assertThrows( + IllegalStateException.class, () -> NullablePropertyWithBuilder.builder().build()); + assertThat(e).hasMessageThat().contains("notNullable"); } @AutoValue @@ -470,13 +465,41 @@ public class AutoValueJava8Test { assertThat(instance3.notOptional()).isEqualTo("hello"); assertThat(instance3.optional()).hasValue("world"); - try { - OptionalPropertyWithNullableBuilder.builder().build(); - fail("Expected IllegalStateException for unset non-Optional property"); - } catch (IllegalStateException expected) { + assertThrows( + IllegalStateException.class, () -> OptionalPropertyWithNullableBuilder.builder().build()); + } + + @AutoValue + public abstract static class NullableOptionalPropertyWithNullableBuilder { + public abstract @Nullable Optional<String> optional(); + + public static Builder builder() { + return new AutoValue_AutoValueJava8Test_NullableOptionalPropertyWithNullableBuilder.Builder(); + } + + @AutoValue.Builder + public interface Builder { + Builder optional(@Nullable String s); + + NullableOptionalPropertyWithNullableBuilder build(); } } + @Test + public void testNullableOptional() { + NullableOptionalPropertyWithNullableBuilder instance1 = + NullableOptionalPropertyWithNullableBuilder.builder().build(); + assertThat(instance1.optional()).isNull(); + + NullableOptionalPropertyWithNullableBuilder instance2 = + NullableOptionalPropertyWithNullableBuilder.builder().optional(null).build(); + assertThat(instance2.optional()).isEmpty(); + + NullableOptionalPropertyWithNullableBuilder instance3 = + NullableOptionalPropertyWithNullableBuilder.builder().optional("haruspex").build(); + assertThat(instance3.optional()).hasValue("haruspex"); + } + @AutoValue @SuppressWarnings("AutoValueImmutableFields") public abstract static class BuilderWithUnprefixedGetters<T extends Comparable<T>> { @@ -522,18 +545,10 @@ public class AutoValueJava8Test { BuilderWithUnprefixedGetters.Builder<String> builder = BuilderWithUnprefixedGetters.builder(); assertThat(builder.t()).isNull(); - try { - builder.list(); - fail("Attempt to retrieve unset list property should have failed"); - } catch (IllegalStateException e) { - assertThat(e).hasMessageThat().isEqualTo("Property \"list\" has not been set"); - } - try { - builder.ints(); - fail("Attempt to retrieve unset ints property should have failed"); - } catch (IllegalStateException e) { - assertThat(e).hasMessageThat().isEqualTo("Property \"ints\" has not been set"); - } + IllegalStateException e1 = assertThrows(IllegalStateException.class, () -> builder.list()); + assertThat(e1).hasMessageThat().isEqualTo("Property \"list\" has not been set"); + IllegalStateException e2 = assertThrows(IllegalStateException.class, () -> builder.ints()); + assertThat(e2).hasMessageThat().isEqualTo("Property \"ints\" has not been set"); builder.setList(names); assertThat(builder.list()).isSameInstanceAs(names); @@ -596,12 +611,8 @@ public class AutoValueJava8Test { BuilderWithPrefixedGetters.Builder<String> builder = BuilderWithPrefixedGetters.builder(); assertThat(builder.getInts()).isNull(); - try { - builder.getList(); - fail("Attempt to retrieve unset list property should have failed"); - } catch (IllegalStateException e) { - assertThat(e).hasMessageThat().isEqualTo("Property \"list\" has not been set"); - } + IllegalStateException e = assertThrows(IllegalStateException.class, () -> builder.getList()); + assertThat(e).hasMessageThat().isEqualTo("Property \"list\" has not been set"); builder.setList(names); assertThat(builder.getList()).isSameInstanceAs(names); diff --git a/value/src/it/gwtserializer/pom.xml b/value/src/it/gwtserializer/pom.xml index cd56484e..eea89528 100644 --- a/value/src/it/gwtserializer/pom.xml +++ b/value/src/it/gwtserializer/pom.xml @@ -22,14 +22,14 @@ <parent> <groupId>com.google.auto.value</groupId> <artifactId>auto-value-parent</artifactId> - <version>HEAD-SNAPSHOT</version> + <version>1.7.4</version> <relativePath>../../../pom.xml</relativePath> </parent> <url>https://github.com/google/auto/tree/master/value</url> <groupId>com.google.auto.value.it.gwtserializer</groupId> <artifactId>gwtserializer</artifactId> - <version>HEAD-SNAPSHOT</version> + <version>1.7.4</version> <name>Auto-Value GWT-RPC Serialization Integration Test</name> <dependencyManagement> <dependencies> diff --git a/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java b/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java index db2f2212..0ca46bde 100644 --- a/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java +++ b/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java @@ -100,8 +100,11 @@ public final class MemoizeExtension extends AutoValueExtension { private static final String AUTO_VALUE_NAME = AUTO_VALUE_PACKAGE_NAME + "AutoValue"; private static final String COPY_ANNOTATIONS_NAME = AUTO_VALUE_NAME + ".CopyAnnotations"; + // Maven is configured to shade (rewrite) com.google packages to prevent dependency conflicts. + // Split up the package here with a call to concat to prevent Maven from finding and rewriting it, + // so that this will be able to find the LazyInit annotation if it's on the classpath. private static final ClassName LAZY_INIT = - ClassName.get("com.google.errorprone.annotations.concurrent", "LazyInit"); + ClassName.get("com".concat(".google.errorprone.annotations.concurrent"), "LazyInit"); private static final AnnotationSpec SUPPRESS_WARNINGS = AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "$S", "Immutable").build(); diff --git a/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java index d05ed43c..d73c1c29 100644 --- a/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java +++ b/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java @@ -72,7 +72,9 @@ public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor { void processType(TypeElement autoOneOfType) { if (autoOneOfType.getKind() != ElementKind.CLASS) { errorReporter() - .abortWithError(autoOneOfType, "@" + AUTO_ONE_OF_NAME + " only applies to classes"); + .abortWithError( + autoOneOfType, + "[AutoOneOfNotClass] @" + AUTO_ONE_OF_NAME + " only applies to classes"); } checkModifiersIfNested(autoOneOfType); DeclaredType kindMirror = mirrorForKindType(autoOneOfType); @@ -129,7 +131,7 @@ public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor { errorReporter() .abortWithError( autoOneOfType, - "annotation processor for @AutoOneOf was invoked with a type" + "[AutoOneOfCompilerBug] annotation processor for @AutoOneOf was invoked with a type" + " that does not have that annotation; this is probably a compiler bug"); } AnnotationValue kindValue = @@ -184,7 +186,8 @@ public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor { errorReporter() .reportError( kindElement, - "Enum has no constant with name corresponding to property '%s'", + "[AutoOneOfNoEnumConstant] Enum has no constant with name corresponding to" + + " property '%s'", property); } }); @@ -195,7 +198,8 @@ public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor { errorReporter() .reportError( constant, - "Name of enum constant '%s' does not correspond to any property name", + "[AutoOneOfBadEnumConstant] Name of enum constant '%s' does not correspond to" + + " any property name", constant.getSimpleName()); } }); @@ -221,7 +225,7 @@ public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor { errorReporter() .reportError( autoOneOfType, - "%s must have a no-arg abstract method returning %s", + "[AutoOneOfNoKindGetter] %s must have a no-arg abstract method returning %s", autoOneOfType, kindMirror); break; @@ -230,7 +234,10 @@ public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor { default: for (ExecutableElement getter : kindGetters) { errorReporter() - .reportError(getter, "More than one abstract method returns %s", kindMirror); + .reportError( + getter, + "[AutoOneOfTwoKindGetters] More than one abstract method returns %s", + kindMirror); } } throw new AbortProcessingException(); @@ -254,7 +261,8 @@ public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor { // implement this alien method. errorReporter() .reportWarning( - method, "Abstract methods in @AutoOneOf classes must have no parameters"); + method, + "[AutoOneOfParams] Abstract methods in @AutoOneOf classes must have no parameters"); } } errorReporter().abortIfAnyError(); @@ -278,7 +286,9 @@ public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor { @Override Optional<String> nullableAnnotationForMethod(ExecutableElement propertyMethod) { if (nullableAnnotationFor(propertyMethod, propertyMethod.getReturnType()).isPresent()) { - errorReporter().reportError(propertyMethod, "@AutoOneOf properties cannot be @Nullable"); + errorReporter() + .reportError( + propertyMethod, "[AutoOneOfNullable] @AutoOneOf properties cannot be @Nullable"); } return Optional.empty(); } diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfProcessor.java index 4214c128..36a82bd4 100644 --- a/value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfProcessor.java +++ b/value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfProcessor.java @@ -71,7 +71,6 @@ import javax.lang.model.element.QualifiedNameable; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; -import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; @@ -301,7 +300,8 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor { for (TypeElement type : deferredTypes) { errorReporter.reportError( type, - "Did not generate @%s class for %s because it references undefined types", + "[AutoValueUndefined] Did not generate @%s class for %s because it references" + + " undefined types", simpleAnnotationName, type.getQualifiedName()); } @@ -331,7 +331,10 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor { } catch (RuntimeException e) { String trace = Throwables.getStackTraceAsString(e); errorReporter.reportError( - type, "@%s processor threw an exception: %s", simpleAnnotationName, trace); + type, + "[AutoValueException] @%s processor threw an exception: %s", + simpleAnnotationName, + trace); throw e; } } @@ -378,8 +381,9 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor { ImmutableSet.Builder<Property> props = ImmutableSet.builder(); propertyMethodsAndTypes.forEach( (propertyMethod, returnType) -> { - String propertyType = TypeEncoder.encodeWithAnnotations( - returnType, getExcludedAnnotationTypes(propertyMethod)); + String propertyType = + TypeEncoder.encodeWithAnnotations( + returnType, getExcludedAnnotationTypes(propertyMethod)); String propertyName = methodToPropertyName.get(propertyMethod); String identifier = methodToIdentifier.get(propertyMethod); ImmutableList<String> fieldAnnotations = @@ -399,7 +403,9 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor { nullableAnnotation); props.add(p); if (p.isNullable() && returnType.getKind().isPrimitive()) { - errorReporter().reportError(propertyMethod, "Primitive types cannot be @Nullable"); + errorReporter() + .reportError( + propertyMethod, "[AutoValueNullPrimitive] Primitive types cannot be @Nullable"); } }); return props.build(); @@ -517,7 +523,10 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor { // reportedDups prevents us from reporting more than one error for the same method. for (ExecutableElement context : contexts) { errorReporter.reportError( - context, "More than one @%s property called %s", simpleAnnotationName, name); + context, + "[AutoValueDupProperty] More than one @%s property called %s", + simpleAnnotationName, + name); } } } @@ -622,16 +631,18 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor { if (enclosingKind.isClass() || enclosingKind.isInterface()) { if (type.getModifiers().contains(Modifier.PRIVATE)) { errorReporter.abortWithError( - type, "@%s class must not be private", simpleAnnotationName); + type, "[AutoValuePrivate] @%s class must not be private", simpleAnnotationName); } else if (Visibility.effectiveVisibilityOfElement(type).equals(Visibility.PRIVATE)) { // The previous case, where the class itself is private, is much commoner so it deserves // its own error message, even though it would be caught by the test here too. errorReporter.abortWithError( - type, "@%s class must not be nested in a private class", simpleAnnotationName); + type, + "[AutoValueInPrivate] @%s class must not be nested in a private class", + simpleAnnotationName); } if (!type.getModifiers().contains(Modifier.STATIC)) { errorReporter.abortWithError( - type, "Nested @%s class must be static", simpleAnnotationName); + type, "[AutoValueInner] Nested @%s class must be static", simpleAnnotationName); } } // In principle type.getEnclosingElement() could be an ExecutableElement (for a class @@ -766,13 +777,14 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor { final void checkReturnType(TypeElement autoValueClass, ExecutableElement getter) { TypeMirror type = getter.getReturnType(); if (type.getKind() == TypeKind.ARRAY) { - TypeMirror componentType = ((ArrayType) type).getComponentType(); - if (componentType.getKind().isPrimitive()) { + TypeMirror componentType = MoreTypes.asArray(type).getComponentType(); + if (componentType.getKind().isPrimitive()) { warnAboutPrimitiveArrays(autoValueClass, getter); } else { errorReporter.reportError( getter, - "An @%s class cannot define an array-valued property unless it is a primitive array", + "[AutoValueArray] An @%s class cannot define an array-valued property unless it is a" + + " primitive array", simpleAnnotationName); } } @@ -799,10 +811,11 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor { String context = sameClass ? "" : (" Method: " + getter.getEnclosingElement() + "." + getter); errorReporter.reportWarning( element, - "An @%s property that is a primitive array returns the original array, which can" - + " therefore be modified by the caller. If this is OK, you can suppress this warning" - + " with @SuppressWarnings(\"mutable\"). Otherwise, you should replace the property" - + " with an immutable type, perhaps a simple wrapper around the original array.%s", + "[AutoValueMutable] An @%s property that is a primitive array returns the original" + + " array, which can therefore be modified by the caller. If this is OK, you can" + + " suppress this warning with @SuppressWarnings(\"mutable\"). Otherwise, you should" + + " replace the property with an immutable type, perhaps a simple wrapper around the" + + " original array.%s", simpleAnnotationName, context); } @@ -815,8 +828,8 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor { private static class ContainsMutableVisitor extends SimpleAnnotationValueVisitor8<Boolean, Void> { @Override public Boolean visitArray(List<? extends AnnotationValue> list, Void p) { - return list.stream().map(av -> av.getValue()).anyMatch("mutable"::equals); - } + return list.stream().map(AnnotationValue::getValue).anyMatch("mutable"::equals); + } } /** @@ -1102,7 +1115,10 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor { // error later because user code will have a reference to the code we were supposed to // generate (new AutoValue_Foo() or whatever) and that reference will be undefined. errorReporter.reportWarning( - originatingType, "Could not write generated class %s: %s", className, e); + originatingType, + "[AutoValueCouldNotWrite] Could not write generated class %s: %s", + className, + e); } } } diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java index f3b396ca..aafffd7c 100644 --- a/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java +++ b/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java @@ -121,8 +121,8 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor { errorReporter() .reportWarning( null, - "An exception occurred while looking for AutoValue extensions." - + " No extensions will function.%s\n%s", + "[AutoValueExtensionsException] An exception occurred while looking for AutoValue" + + " extensions. No extensions will function.%s\n%s", explain, Throwables.getStackTraceAsString(e)); extensions = ImmutableList.of(); @@ -170,19 +170,22 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor { errorReporter() .abortWithError( type, - "annotation processor for @AutoValue was invoked with a type" + "[AutoValueCompilerBug] annotation processor for @AutoValue was invoked with a type" + " that does not have that annotation; this is probably a compiler bug"); } if (type.getKind() != ElementKind.CLASS) { - errorReporter().abortWithError(type, "@AutoValue only applies to classes"); + errorReporter() + .abortWithError(type, "[AutoValueNotClass] @AutoValue only applies to classes"); } if (ancestorIsAutoValue(type)) { - errorReporter().abortWithError(type, "One @AutoValue class may not extend another"); + errorReporter() + .abortWithError(type, "[AutoValueExtend] One @AutoValue class may not extend another"); } if (implementsAnnotation(type)) { errorReporter() .abortWithError( - type, "@AutoValue may not be used to implement an annotation" + type, + "[AutoValueImplAnnotation] @AutoValue may not be used to implement an annotation" + " interface; try using @AutoAnnotation instead"); } checkModifiersIfNested(type); @@ -333,7 +336,8 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor { errorReporter() .reportError( type, - "More than one extension wants to generate the final class: %s", + "[AutoValueMultiFinal] More than one extension wants to generate the final class:" + + " %s", finalExtensions.stream().map(this::extensionName).collect(joining(", "))); break; } @@ -355,7 +359,8 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor { errorReporter() .reportError( type, - "Extension %s wants to consume a property that does not exist: %s", + "[AutoValueConsumeNonexist] Extension %s wants to consume a property that does" + + " not exist: %s", extensionName(extension), consumedProperty); } else { @@ -367,8 +372,8 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor { errorReporter() .reportError( type, - "Extension %s wants to consume a method that is not one of the abstract methods" - + " in this class: %s", + "[AutoValueConsumeNotAbstract] Extension %s wants to consume a method that is" + + " not one of the abstract methods in this class: %s", extensionName(extension), consumedMethod); } else { @@ -379,8 +384,8 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor { errorReporter() .reportError( repeat, - "Extension %s wants to consume a method that was already consumed by another" - + " extension", + "[AutoValueMultiConsume] Extension %s wants to consume a method that was already" + + " consumed by another extension", extensionName(extension)); } consumed.addAll(consumedHere); @@ -404,10 +409,12 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor { // another, and therefore leaves us with both an abstract method and the subclass method // that overrides it. This shows up in AutoValueTest.LukesBase for example. String extensionMessage = extensionsPresent ? ", and no extension consumed it" : ""; - errorReporter().reportWarning( - method, - "Abstract method is neither a property getter nor a Builder converter%s", - extensionMessage); + errorReporter() + .reportWarning( + method, + "[AutoValueBuilderWhat] Abstract method is neither a property getter nor a Builder" + + " converter%s", + extensionMessage); } } errorReporter().abortIfAnyError(); diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java index 724a3225..81751e30 100644 --- a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java +++ b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java @@ -20,6 +20,7 @@ import static com.google.common.collect.Sets.difference; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; +import com.google.auto.value.processor.BuilderSpec.Copier; import com.google.auto.value.processor.BuilderSpec.PropertySetter; import com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder; import com.google.common.base.Equivalence; @@ -197,7 +198,7 @@ class BuilderMethodClassifier { } else { errorReporter.reportError( propertyNameToUnprefixedSetters.values().iterator().next().getSetter(), - "If any setter methods use the setFoo convention then all must"); + "[AutoValueSetNotSet] If any setter methods use the setFoo convention then all must"); return false; } getterToPropertyName.forEach( @@ -219,10 +220,10 @@ class BuilderMethodClassifier { if (needToMakeBarBuilder && !canMakeBarBuilder) { errorReporter.reportError( propertyBuilder.getPropertyBuilderMethod(), - "Property builder method returns %1$s but there is no way to make that type" - + " from %2$s: %2$s does not have a non-static toBuilder() method that" - + " returns %1$s, and %1$s does not have a method addAll or" - + " putAll that accepts an argument of type %2$s", + "[AutoValueCantMakeBuilder] Property builder method returns %1$s but there is no" + + " way to make that type from %2$s: %2$s does not have a non-static" + + " toBuilder() method that returns %1$s, and %1$s does not have a method" + + " addAll or putAll that accepts an argument of type %2$s", propertyBuilder.getBuilderTypeMirror(), propertyType); } @@ -231,8 +232,13 @@ class BuilderMethodClassifier { String setterName = settersPrefixed ? prefixWithSet(property) : property; errorReporter.reportError( builderType, - "Expected a method with this signature: %s%s %s(%s), or a %sBuilder() method", - builderType, typeParamsString(), setterName, propertyType, property); + "[AutoValueBuilderMissingMethod] Expected a method with this signature: %s%s" + + " %s(%s), or a %sBuilder() method", + builderType, + typeParamsString(), + setterName, + propertyType, + property); } }); return errorReporter.errorCount() == startErrorCount; @@ -248,7 +254,8 @@ class BuilderMethodClassifier { classifyMethodOneArg(method); break; default: - errorReporter.reportError(method, "Builder methods must have 0 or 1 parameters"); + errorReporter.reportError( + method, "[AutoValueBuilderArgs] Builder methods must have 0 or 1 parameters"); } } @@ -296,10 +303,11 @@ class BuilderMethodClassifier { } else { errorReporter.reportError( method, - "Method without arguments should be a build method returning %1$s%2$s," - + " or a getter method with the same name and type as a getter method of %1$s," - + " or fooBuilder() where foo() or getFoo() is a getter method of %1$s", - autoValueClass, typeParamsString()); + "[AutoValueBuilderNoArg] Method without arguments should be a build method returning" + + " %1$s%2$s, or a getter method with the same name and type as a getter method of" + + " %1$s, or fooBuilder() where foo() or getFoo() is a getter method of %1$s", + autoValueClass, + typeParamsString()); } } @@ -334,9 +342,11 @@ class BuilderMethodClassifier { } errorReporter.reportError( builderGetter, - "Method matches a property of %1$s but has return type %2$s instead of %3$s " - + "or an Optional wrapping of %3$s", - autoValueClass, builderGetterType, originalGetterType); + "[AutoValueBuilderReturnType] Method matches a property of %1$s but has return type %2$s" + + " instead of %3$s or an Optional wrapping of %3$s", + autoValueClass, + builderGetterType, + originalGetterType); } /** @@ -372,11 +382,13 @@ class BuilderMethodClassifier { // The second disjunct isn't needed but convinces control-flow checkers that // propertyNameToSetters can't be null when we call put on it below. errorReporter.reportError( - method, "Method does not correspond to a property of %s", autoValueClass); + method, + "[AutoValueBuilderWhatProp] Method does not correspond to a property of %s", + autoValueClass); checkForFailedJavaBean(method); return; } - Optional<Function<String, String>> function = getSetterFunction(valueGetter, method); + Optional<Copier> function = getSetterFunction(valueGetter, method); if (function.isPresent()) { DeclaredType builderTypeMirror = MoreTypes.asDeclared(builderType.asType()); ExecutableType methodMirror = @@ -419,7 +431,7 @@ class BuilderMethodClassifier { * using a method like {@code ImmutableList.copyOf} or {@code Optional.of}, when the returned * function will be something like {@code s -> "Optional.of(" + s + ")"}. */ - private Optional<Function<String, String>> getSetterFunction( + private Optional<Copier> getSetterFunction( ExecutableElement valueGetter, ExecutableElement setter) { VariableElement parameterElement = Iterables.getOnlyElement(setter.getParameters()); boolean nullableParameter = @@ -440,12 +452,14 @@ class BuilderMethodClassifier { if (!nullableProperty) { errorReporter.reportError( setter, - "Parameter of setter method is @Nullable but property method %s.%s() is not", - autoValueClass, valueGetter.getSimpleName()); + "[AutoValueNullNotNull] Parameter of setter method is @Nullable but property method" + + " %s.%s() is not", + autoValueClass, + valueGetter.getSimpleName()); return Optional.empty(); } } - return Optional.of(s -> s); + return Optional.of(Copier.IDENTITY); } // Parameter type is not equal to property type, but might be convertible with copyOf. @@ -455,7 +469,7 @@ class BuilderMethodClassifier { } errorReporter.reportError( setter, - "Parameter type %s of setter method should be %s to match getter %s.%s", + "[AutoValueGetVsSet] Parameter type %s of setter method should be %s to match getter %s.%s", parameterType, targetType, autoValueClass, valueGetter.getSimpleName()); return Optional.empty(); } @@ -465,14 +479,14 @@ class BuilderMethodClassifier { * to the getter's return type using one of the given methods, or {@code Optional.empty()} if the * conversion isn't possible. An error will have been reported in the latter case. */ - private Optional<Function<String, String>> getConvertingSetterFunction( + private Optional<Copier> getConvertingSetterFunction( ImmutableList<ExecutableElement> copyOfMethods, ExecutableElement valueGetter, ExecutableElement setter, TypeMirror parameterType) { DeclaredType targetType = MoreTypes.asDeclared(getterToPropertyType.get(valueGetter)); for (ExecutableElement copyOfMethod : copyOfMethods) { - Optional<Function<String, String>> function = + Optional<Copier> function = getConvertingSetterFunction(copyOfMethod, targetType, parameterType); if (function.isPresent()) { return function; @@ -481,8 +495,8 @@ class BuilderMethodClassifier { String targetTypeSimpleName = targetType.asElement().getSimpleName().toString(); errorReporter.reportError( setter, - "Parameter type %s of setter method should be %s to match getter %s.%s," - + " or it should be a type that can be passed to %s.%s to produce %s", + "[AutoValueGetVsSetOrConvert] Parameter type %s of setter method should be %s to match" + + " getter %s.%s, or it should be a type that can be passed to %s.%s to produce %s", parameterType, targetType, autoValueClass, @@ -516,7 +530,7 @@ class BuilderMethodClassifier { * @return a function that maps a string parameter to a method call using that parameter. For * example it might map {@code foo} to {@code ImmutableList.copyOf(foo)}. */ - private Optional<Function<String, String>> getConvertingSetterFunction( + private Optional<Copier> getConvertingSetterFunction( ExecutableElement copyOfMethod, DeclaredType targetType, TypeMirror parameterType) { // We have a parameter type, for example Set<? extends T>, and we want to know if it can be // passed to the given copyOf method, which might for example be one of these methods from @@ -532,7 +546,15 @@ class BuilderMethodClassifier { if (TypeVariables.canAssignStaticMethodResult( copyOfMethod, parameterType, targetType, typeUtils)) { String method = TypeEncoder.encodeRaw(targetType) + "." + copyOfMethod.getSimpleName(); - return Optional.of(s -> method + "(" + s + ")"); + Function<String, String> callMethod = s -> method + "(" + s + ")"; + // This is a big old hack. We guess that the method can accept a null parameter if it has + // "Nullable" in the name, which java.util.Optional.ofNullable and + // com.google.common.base.Optional.fromNullable do. + Copier copier = + method.contains("Nullable") + ? Copier.acceptingNull(callMethod) + : Copier.notAcceptingNull(callMethod); + return Optional.of(copier); } return Optional.empty(); } diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java b/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java index 613d5457..7e5b17c9 100644 --- a/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java +++ b/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java @@ -86,14 +86,17 @@ class BuilderSpec { if (hasAnnotationMirror(containedClass, AUTO_VALUE_BUILDER_NAME)) { if (!CLASS_OR_INTERFACE.contains(containedClass.getKind())) { errorReporter.reportError( - containedClass, "@AutoValue.Builder can only apply to a class or an interface"); + containedClass, + "[AutoValueBuilderClass] @AutoValue.Builder can only apply to a class or an" + + " interface"); } else if (!containedClass.getModifiers().contains(Modifier.STATIC)) { errorReporter.reportError( - containedClass, "@AutoValue.Builder cannot be applied to a non-static class"); + containedClass, + "[AutoValueInnerBuilder] @AutoValue.Builder cannot be applied to a non-static class"); } else if (builderTypeElement.isPresent()) { errorReporter.reportError( containedClass, - "%s already has a Builder: %s", + "[AutoValueTwoBuilders] %s already has a Builder: %s", autoValueClass, builderTypeElement.get()); } else { @@ -218,7 +221,7 @@ class BuilderSpec { if (!builderTypeParamNames.equals(typeArguments)) { errorReporter.reportError( method, - "Builder converter method should return %s%s", + "[AutoValueBuilderConverterReturn] Builder converter method should return %s%s", builderTypeElement, TypeSimplifier.actualTypeParametersString(builderTypeElement)); } @@ -227,7 +230,8 @@ class BuilderSpec { ImmutableSet<ExecutableElement> builderMethods = methods.build(); if (builderMethods.size() > 1) { errorReporter.reportError( - builderMethods.iterator().next(), "There can be at most one builder converter method"); + builderMethods.iterator().next(), + "[AutoValueTwoBuilderConverters] There can be at most one builder converter method"); } this.toBuilderMethods = builderMethods; return builderMethods; @@ -265,7 +269,9 @@ class BuilderSpec { // For now we ignore methods with annotations, because for example we do want to allow // Jackson's @JsonCreator. errorReporter.reportWarning( - method, "Static builder() method should be in the containing class"); + method, + "[AutoValueBuilderInBuilder] Static builder() method should be in the containing" + + " class"); } } this.classifier = optionalClassifier.get(); @@ -276,7 +282,8 @@ class BuilderSpec { for (Element buildMethod : errorElements) { errorReporter.reportError( buildMethod, - "Builder must have a single no-argument method returning %s%s", + "[AutoValueBuilderBuild] Builder must have a single no-argument method returning" + + " %s%s", autoValueClass, typeParamsString()); } @@ -354,6 +361,30 @@ class BuilderSpec { } /** + * Specifies how to copy a parameter value into the target type. This might be the identity, or + * it might be something like {@code ImmutableList.of(...)} or {@code Optional.ofNullable(...)}. + */ + static class Copier { + static final Copier IDENTITY = acceptingNull(x -> x); + + private final Function<String, String> copy; + private final boolean acceptsNull; + + private Copier(Function<String, String> copy, boolean acceptsNull) { + this.copy = copy; + this.acceptsNull = acceptsNull; + } + + static Copier acceptingNull(Function<String, String> copy) { + return new Copier(copy, true); + } + + static Copier notAcceptingNull(Function<String, String> copy) { + return new Copier(copy, false); + } + } + + /** * Information about a property setter, referenced from the autovalue.vm template. A property * called foo (defined by a method {@code T foo()} or {@code T getFoo()}) can have a setter method * {@code foo(T)} or {@code setFoo(T)} that returns the builder type. Additionally, it can have a @@ -369,12 +400,11 @@ class BuilderSpec { private final String parameterTypeString; private final boolean primitiveParameter; private final String nullableAnnotation; - private final Function<String, String> copyFunction; + private final Copier copier; - PropertySetter( - ExecutableElement setter, TypeMirror parameterType, Function<String, String> copyFunction) { + PropertySetter(ExecutableElement setter, TypeMirror parameterType, Copier copier) { this.setter = setter; - this.copyFunction = copyFunction; + this.copier = copier; this.access = SimpleMethod.access(setter); this.name = setter.getSimpleName().toString(); primitiveParameter = parameterType.getKind().isPrimitive(); @@ -423,13 +453,10 @@ class BuilderSpec { } public String copy(AutoValueProcessor.Property property) { - String copy = copyFunction.apply(property.toString()); - - // Add a null guard only in cases where we are using copyOf and the property is @Nullable. - if (property.isNullable() && !copy.equals(property.toString())) { + String copy = copier.copy.apply(property.toString()); + if (property.isNullable() && !copier.acceptsNull) { copy = String.format("(%s == null ? null : %s)", property, copy); } - return copy; } } @@ -450,7 +477,8 @@ class BuilderSpec { if (!sameTypeParameters(autoValueClass, builderTypeElement)) { errorReporter.reportError( builderTypeElement, - "Type parameters of %s must have same names and bounds as type parameters of %s", + "[AutoValueTypeParamMismatch] Type parameters of %s must have same names and bounds as" + + " type parameters of %s", builderTypeElement, autoValueClass); return Optional.empty(); diff --git a/value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java b/value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java index dafb5820..2565cddc 100644 --- a/value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java +++ b/value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java @@ -198,7 +198,8 @@ class PropertyBuilderClassifier { if (barBuilderTypeMirror.getKind() != TypeKind.DECLARED) { errorReporter.reportError( method, - "Method looks like a property builder, but its return type is not a class or interface"); + "[AutoValueOddBuilderMethod] Method looks like a property builder, but its return type" + + " is not a class or interface"); return Optional.empty(); } DeclaredType barBuilderDeclaredType = MoreTypes.asDeclared(barBuilderTypeMirror); @@ -210,15 +211,15 @@ class PropertyBuilderClassifier { if (barTypeMirror.getKind() != TypeKind.DECLARED) { errorReporter.reportError( method, - "Method looks like a property builder, but the type of property %s is not a class or" - + " interface", + "[AutoValueBadBuilderMethod] Method looks like a property builder, but the type of" + + " property %s is not a class or interface", property); return Optional.empty(); } if (isNullable(barGetter)) { errorReporter.reportError( barGetter, - "Property %s has a property builder so it cannot be @Nullable", + "[AutoValueNullBuilder] Property %s has a property builder so it cannot be @Nullable", property); } TypeElement barTypeElement = MoreTypes.asTypeElement(barTypeMirror); @@ -229,8 +230,8 @@ class PropertyBuilderClassifier { if (build == null || build.getModifiers().contains(Modifier.STATIC)) { errorReporter.reportError( method, - "Method looks like a property builder, but it returns %s which does not have a" - + " non-static build() method", + "[AutoValueBuilderNotBuildable] Method looks like a property builder, but it returns %s" + + " which does not have a non-static build() method", barBuilderTypeElement); return Optional.empty(); } @@ -241,7 +242,8 @@ class PropertyBuilderClassifier { if (!MoreTypes.equivalence().equivalent(barTypeMirror, buildType)) { errorReporter.reportError( method, - "Property builder for %s has type %s whose build() method returns %s instead of %s", + "[AutoValueBuilderWrongType] Property builder for %s has type %s whose build() method" + + " returns %s instead of %s", property, barBuilderTypeElement, buildType, @@ -254,9 +256,9 @@ class PropertyBuilderClassifier { if (!maybeBuilderMaker.isPresent()) { errorReporter.reportError( method, - "Method looks like a property builder, but its type %s does not have a public" - + " constructor and %s does not have a static builder() or newBuilder() method that" - + " returns %s", + "[AutoValueCantMakePropertyBuilder] Method looks like a property builder, but its type" + + " %s does not have a public constructor and %s does not have a static builder() or" + + " newBuilder() method that returns %s", barBuilderTypeElement, barTypeElement, barBuilderTypeElement); diff --git a/value/src/main/java/com/google/auto/value/processor/autoannotation.vm b/value/src/main/java/com/google/auto/value/processor/autoannotation.vm index 1d14d3fb..a953edab 100644 --- a/value/src/main/java/com/google/auto/value/processor/autoannotation.vm +++ b/value/src/main/java/com/google/auto/value/processor/autoannotation.vm @@ -108,7 +108,7 @@ final class $className implements $annotationName { ## annotationType method (defined by the Annotation interface) - @Override + @`java.lang.Override` public Class<? extends $annotationName> annotationType() { return ${annotationName}.class; } @@ -117,7 +117,7 @@ final class $className implements $annotationName { #foreach ($m in $members) - @Override + @`java.lang.Override` public ${m.type} ${m}() { #if ($m.kind == "ARRAY") @@ -162,7 +162,7 @@ final class $className implements $annotationName { #end #end - @Override + @`java.lang.Override` public String toString() { StringBuilder sb = new StringBuilder("@$annotationFullName("); @@ -211,7 +211,7 @@ final class $className implements $annotationName { #end #end - @Override + @`java.lang.Override` public boolean equals(Object o) { if (o == this) { return true; @@ -269,7 +269,7 @@ final class $className implements $annotationName { ## example.) We precompute the invariable part, as an optimization but also in order to avoid ## falling afoul of constant-overflow checks in the compiler. - @Override + @`java.lang.Override` public int hashCode() { return ## If the invariable part is 0, we avoid outputting `return 0 + ...` just because it generates diff --git a/value/src/main/java/com/google/auto/value/processor/autooneof.vm b/value/src/main/java/com/google/auto/value/processor/autooneof.vm index 04cc1949..0249d9eb 100644 --- a/value/src/main/java/com/google/auto/value/processor/autooneof.vm +++ b/value/src/main/java/com/google/auto/value/processor/autooneof.vm @@ -99,7 +99,7 @@ final class $generatedClass { #foreach ($p in $props) - @Override + @`java.lang.Override` $p.access $p.type ${p.getter}() { throw new UnsupportedOperationException(${kindGetter}().toString()); } @@ -128,7 +128,7 @@ final class $generatedClass { private Impl_$p() {} - @Override + @`java.lang.Override` public void ${p.getter}() {} #if ($serializable) @@ -141,7 +141,7 @@ final class $generatedClass { #if ($toString) - @Override + @`java.lang.Override` public String toString() { return "${simpleClassName}{$p.name}"; } @@ -154,7 +154,7 @@ final class $generatedClass { #if ($equals) - @Override + @`java.lang.Override` public boolean equals($equalsParameterType x) { return x == this; } @@ -163,7 +163,7 @@ final class $generatedClass { #if ($hashCode) - @Override + @`java.lang.Override` public int hashCode() { return System.identityHashCode(this); } @@ -178,14 +178,14 @@ final class $generatedClass { this.$p = $p; } - @Override + @`java.lang.Override` public $p.type ${p.getter}() { return $p; } #if ($toString) - @Override + @`java.lang.Override` public String toString() { return "${simpleClassName}{$p.name=" ## + #if ($p.kind == "ARRAY") `java.util.Arrays`.toString(this.$p) #else this.$p #end @@ -196,7 +196,7 @@ final class $generatedClass { #if ($equals) - @Override + @`java.lang.Override` public boolean equals($equalsParameterType x) { if (x instanceof $origClass) { $origClass$wildcardTypes that = ($origClass$wildcardTypes) x; @@ -211,7 +211,7 @@ final class $generatedClass { #if ($hashCode) - @Override + @`java.lang.Override` public int hashCode() { return #hashCodeExpression($p); } @@ -220,7 +220,7 @@ final class $generatedClass { #end - @Override + @`java.lang.Override` public $kindType ${kindGetter}() { return ${kindType}.$propertyToKind[$p.name]; } diff --git a/value/src/main/java/com/google/auto/value/processor/autovalue.vm b/value/src/main/java/com/google/auto/value/processor/autovalue.vm index 80ef79ae..6f50e934 100644 --- a/value/src/main/java/com/google/auto/value/processor/autovalue.vm +++ b/value/src/main/java/com/google/auto/value/processor/autovalue.vm @@ -101,7 +101,7 @@ ${modifiers}class $subclass$formalTypes extends $origClass$actualTypes { ${a}## #end - @Override + @`java.lang.Override` ${p.access}${p.type} ${p.getter}() { return $p; } @@ -110,7 +110,7 @@ ${modifiers}class $subclass$formalTypes extends $origClass$actualTypes { #if ($toString) - @Override + @`java.lang.Override` public `java.lang.String` toString() { return "#if ($identifiers)$simpleClassName#end{" @@ -129,7 +129,7 @@ ${modifiers}class $subclass$formalTypes extends $origClass$actualTypes { #if ($equals) - @Override + @`java.lang.Override` public boolean equals($equalsParameterType o) { if (o == this) { return true; @@ -162,7 +162,7 @@ ${modifiers}class $subclass$formalTypes extends $origClass$actualTypes { #if ($hashCode) - @Override + @`java.lang.Override` public int hashCode() { int h$ = 1; @@ -185,7 +185,7 @@ ${modifiers}class $subclass$formalTypes extends $origClass$actualTypes { #foreach ($m in $toBuilderMethods) - @Override + @`java.lang.Override` ${m.access}${builderTypeName}${builderActualTypes} ${m.name}() { return new Builder${builderActualTypes}(this); } @@ -252,7 +252,7 @@ ${modifiers}class $subclass$formalTypes extends $origClass$actualTypes { #foreach ($setter in $builderSetters[$p.name]) - @Override + @`java.lang.Override` ${setter.access}${builderTypeName}${builderActualTypes} ## ${setter.name}(${setter.nullableAnnotation}$setter.parameterType $p) { @@ -290,7 +290,7 @@ ${modifiers}class $subclass$formalTypes extends $origClass$actualTypes { #if ($propertyBuilder) - @Override + @`java.lang.Override` ${propertyBuilder.access}$propertyBuilder.builderType ${p.name}Builder() { if (${propertyBuilder.name} == null) { @@ -337,7 +337,7 @@ ${modifiers}class $subclass$formalTypes extends $origClass$actualTypes { #if ($builderGetters[$p.name]) - @Override + @`java.lang.Override` ${p.nullableAnnotation}${builderGetters[$p.name].access}$builderGetters[$p.name].type ${p.getter}() { #if ($builderGetters[$p.name].optional) @@ -377,7 +377,7 @@ ${modifiers}class $subclass$formalTypes extends $origClass$actualTypes { #end #end - @Override + @`java.lang.Override` ${buildMethod.get().access}${origClass}${actualTypes} ${buildMethod.get().name}() { #foreach ($p in $props) diff --git a/value/src/main/java/com/google/auto/value/processor/gwtserializer.vm b/value/src/main/java/com/google/auto/value/processor/gwtserializer.vm index 04500360..1265aae4 100644 --- a/value/src/main/java/com/google/auto/value/processor/gwtserializer.vm +++ b/value/src/main/java/com/google/auto/value/processor/gwtserializer.vm @@ -93,26 +93,26 @@ public final class $serializerClass$formalTypes @SuppressWarnings("unused") private int dummy_$classHashString; - @Override + @`java.lang.Override` public void deserializeInstance( `com.google.gwt.user.client.rpc.SerializationStreamReader` streamReader, $subclass$actualTypes instance) { deserialize(streamReader, instance); } - @Override + @`java.lang.Override` public boolean hasCustomInstantiateInstance() { return true; } - @Override + @`java.lang.Override` public $subclass$actualTypes instantiateInstance( `com.google.gwt.user.client.rpc.SerializationStreamReader` streamReader) throws `com.google.gwt.user.client.rpc.SerializationException` { return instantiate(streamReader); } - @Override + @`java.lang.Override` public void serializeInstance( `com.google.gwt.user.client.rpc.SerializationStreamWriter` streamWriter, $subclass$actualTypes instance) diff --git a/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java b/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java index 2d4aa7ca..e46c9f51 100644 --- a/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java +++ b/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java @@ -3189,6 +3189,12 @@ public class AutoValueCompilationTest { "package foo.bar;", "", "public class Thread {}"); + JavaFileObject override = + JavaFileObjects.forSourceLines( + "foo.bar.Override", // + "package foo.bar;", + "", + "public class Override {}"); JavaFileObject test = JavaFileObjects.forSourceLines( "foo.bar.Test", @@ -3215,7 +3221,7 @@ public class AutoValueCompilationTest { javac() .withProcessors(new AutoValueProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") - .compile(object, string, integer, thread, test); + .compile(object, string, integer, thread, override, test); assertThat(compilation).succeededWithoutWarnings(); } diff --git a/value/userguide/howto.md b/value/userguide/howto.md index 1dbc5617..6f9142c1 100644 --- a/value/userguide/howto.md +++ b/value/userguide/howto.md @@ -470,7 +470,7 @@ public abstract class StringOrInteger { } @AutoValue - abstract class StringValue extends StringOrInteger { + abstract static class StringValue extends StringOrInteger { abstract String string(); @Override @@ -480,7 +480,7 @@ public abstract class StringOrInteger { } @AutoValue - abstract class IntegerValue extends StringOrInteger { + abstract static class IntegerValue extends StringOrInteger { abstract int integer(); @Override diff --git a/value/userguide/index.md b/value/userguide/index.md index 8cbe75fc..2e05d54b 100644 --- a/value/userguide/index.md +++ b/value/userguide/index.md @@ -92,6 +92,23 @@ For `auto-value-annotations`, you can write this in `pom.xml`: </dependencies> ``` +Some AutoValue annotations have CLASS retention. This is mostly of use for +compile-time tools, such as AutoValue itself. If you are creating +a library, the end user rarely needs to know the original AutoValue annotations. +In that case, you can set the scope to `provided`, so that the user of your +library does not have `auto-value-annotations` as a transitive dependency. + +```xml +<dependencies> + <dependency> + <groupId>com.google.auto.value</groupId> + <artifactId>auto-value-annotations</artifactId> + <version>${auto-value.version}</version> + <scope>provided</scope> + </dependency> +</dependencies> +``` + For `auto-value` (the annotation processor), you can write this: ```xml |