Include type annotations in bounds of type parameter declarations. For example, if we have `@AutoValue abstract class Foo<T extends @NullableType Object>`, then we should generate `class AutoValue_Foo<T extends @NullableType Object> extends Foo<T>`, rather than just `class AutoValue_Foo<T> extends Foo<T>` as at present.
RELNOTES=In AutoValue code, include type annotations in bounds of type-parameter declarations.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=254752026
diff --git a/value/src/main/java/com/google/auto/value/processor/TypeEncoder.java b/value/src/main/java/com/google/auto/value/processor/TypeEncoder.java
index 8d20a66..912091f 100644
--- a/value/src/main/java/com/google/auto/value/processor/TypeEncoder.java
+++ b/value/src/main/java/com/google/auto/value/processor/TypeEncoder.java
@@ -182,14 +182,21 @@
sb.append(typeParameter.getSimpleName());
String sep = " extends ";
for (TypeMirror bound : typeParameter.getBounds()) {
- if (!bound.toString().equals("java.lang.Object")) {
+ if (!isUnannotatedJavaLangObject(bound)) {
sb.append(sep);
sep = " & ";
- sb.append(encode(bound));
+ sb.append(encodeWithAnnotations(bound));
}
}
}
+ // We can omit "extends Object" from a type bound, but not "extends @NullableType Object".
+ private static boolean isUnannotatedJavaLangObject(TypeMirror type) {
+ return type.getKind().equals(TypeKind.DECLARED)
+ && type.getAnnotationMirrors().isEmpty()
+ && MoreTypes.asTypeElement(type).getQualifiedName().contentEquals("java.lang.Object");
+ }
+
private static void appendAnnotations(
List<? extends AnnotationMirror> annotationMirrors, StringBuilder sb) {
for (AnnotationMirror annotationMirror : annotationMirrors) {
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 134af63..724ea6b 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
@@ -15,11 +15,10 @@
*/
package com.google.auto.value.processor;
-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.CompilationSubject.compilations;
import static com.google.testing.compile.Compiler.javac;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
import static java.util.stream.Collectors.joining;
import com.google.common.collect.ImmutableSet;
@@ -34,9 +33,11 @@
import java.util.stream.Stream;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.util.ElementFilter;
import javax.tools.JavaFileObject;
import org.junit.Rule;
@@ -352,6 +353,75 @@
.hasSourceEquivalentTo(expectedOutput);
}
+ // Tests that type annotations are correctly copied from the bounds of type parameters in the
+ // @AutoValue class to the bounds of the corresponding parameters in the generated class. For
+ // example, if we have `@AutoValue abstract class Foo<T extends @NullableType Object>`, then the
+ // generated class should be `class AutoValue_Foo<T extends @NullableType Object> extends Foo<T>`.
+ // Some buggy versions of javac do not report type annotations correctly in this context.
+ // AutoValue can't copy them if it can't see them, so we make a special annotation processor to
+ // detect if we are in the presence of this bug and if so we don't fail.
+ @Test
+ public void testTypeParametersWithAnnotationsOnBounds() {
+ @SupportedAnnotationTypes("*")
+ class CompilerBugProcessor extends AbstractProcessor {
+ boolean checkedAnnotationsOnTypeBounds;
+ boolean reportsAnnotationsOnTypeBounds;
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (roundEnv.processingOver()) {
+ TypeElement test = processingEnv.getElementUtils().getTypeElement("com.example.Test");
+ TypeParameterElement t = test.getTypeParameters().get(0);
+ this.checkedAnnotationsOnTypeBounds = true;
+ this.reportsAnnotationsOnTypeBounds =
+ !t.getBounds().get(0).getAnnotationMirrors().isEmpty();
+ }
+ return false;
+ }
+ }
+ CompilerBugProcessor compilerBugProcessor = new CompilerBugProcessor();
+ JavaFileObject nullableTypeFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.NullableType",
+ "package foo.bar;",
+ "",
+ "import java.lang.annotation.ElementType;",
+ "import java.lang.annotation.Target;",
+ "",
+ "@Target(ElementType.TYPE_USE)",
+ "public @interface NullableType {}");
+ JavaFileObject autoValueFileObject =
+ JavaFileObjects.forSourceLines(
+ "com.example.Test",
+ "package com.example;",
+ "",
+ "import com.google.auto.value.AutoValue;",
+ "import foo.bar.NullableType;",
+ "",
+ "@AutoValue",
+ "abstract class Test<T extends @NullableType Object & @NullableType Cloneable> {}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoValueProcessor(), compilerBugProcessor)
+ .withOptions("-Xlint:-processing", "-implicit:none")
+ .compile(nullableTypeFileObject, autoValueFileObject);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilerBugProcessor.checkedAnnotationsOnTypeBounds).isTrue();
+ if (compilerBugProcessor.reportsAnnotationsOnTypeBounds) {
+ assertThat(compilation)
+ .generatedSourceFile("com.example.AutoValue_Test")
+ .contentsAsUtf8String()
+ .contains(
+ "class AutoValue_Test<T extends @NullableType Object & @NullableType Cloneable>"
+ + " extends Test<T> {");
+ }
+ }
+
// In the following few tests, see AutoValueProcessor.validateMethods for why unrecognized
// abstract methods provoke only a warning rather than an error. Compilation will fail anyway
// because the generated class is not abstract and does not implement the unrecognized methods.
@@ -2538,12 +2608,13 @@
" Baz<K, V> build();",
" }",
"}");
- assertAbout(javaSource())
- .that(javaFileObject)
- .processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
- .failsToCompile()
- .withErrorContaining("Builder converter method should return foo.bar.Baz.Builder<K, V>")
- .in(javaFileObject)
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
+ .compile(javaFileObject);
+ assertThat(compilation)
+ .hadErrorContaining("Builder converter method should return foo.bar.Baz.Builder<K, V>")
+ .inFile(javaFileObject)
.onLine(9);
}