Skip to content

Commit ca013a2

Browse files
eamonnmcmanusnetdpb
authored andcommitted
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
1 parent 4259261 commit ca013a2

File tree

2 files changed

+88
-10
lines changed

2 files changed

+88
-10
lines changed

value/src/main/java/com/google/auto/value/processor/TypeEncoder.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,21 @@ private static void appendTypeParameterWithBounds(
182182
sb.append(typeParameter.getSimpleName());
183183
String sep = " extends ";
184184
for (TypeMirror bound : typeParameter.getBounds()) {
185-
if (!bound.toString().equals("java.lang.Object")) {
185+
if (!isUnannotatedJavaLangObject(bound)) {
186186
sb.append(sep);
187187
sep = " & ";
188-
sb.append(encode(bound));
188+
sb.append(encodeWithAnnotations(bound));
189189
}
190190
}
191191
}
192192

193+
// We can omit "extends Object" from a type bound, but not "extends @NullableType Object".
194+
private static boolean isUnannotatedJavaLangObject(TypeMirror type) {
195+
return type.getKind().equals(TypeKind.DECLARED)
196+
&& type.getAnnotationMirrors().isEmpty()
197+
&& MoreTypes.asTypeElement(type).getQualifiedName().contentEquals("java.lang.Object");
198+
}
199+
193200
private static void appendAnnotations(
194201
List<? extends AnnotationMirror> annotationMirrors, StringBuilder sb) {
195202
for (AnnotationMirror annotationMirror : annotationMirrors) {

value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,10 @@
1515
*/
1616
package com.google.auto.value.processor;
1717

18-
import static com.google.common.truth.Truth.assertAbout;
18+
import static com.google.common.truth.Truth.assertThat;
1919
import static com.google.testing.compile.CompilationSubject.assertThat;
2020
import static com.google.testing.compile.CompilationSubject.compilations;
2121
import static com.google.testing.compile.Compiler.javac;
22-
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
2322
import static java.util.stream.Collectors.joining;
2423

2524
import com.google.common.collect.ImmutableSet;
@@ -34,9 +33,11 @@
3433
import java.util.stream.Stream;
3534
import javax.annotation.processing.AbstractProcessor;
3635
import javax.annotation.processing.RoundEnvironment;
36+
import javax.annotation.processing.SupportedAnnotationTypes;
3737
import javax.lang.model.SourceVersion;
3838
import javax.lang.model.element.Element;
3939
import javax.lang.model.element.TypeElement;
40+
import javax.lang.model.element.TypeParameterElement;
4041
import javax.lang.model.util.ElementFilter;
4142
import javax.tools.JavaFileObject;
4243
import org.junit.Rule;
@@ -352,6 +353,75 @@ public void testNestedParameterizedTypesWithTypeAnnotations() throws Exception {
352353
.hasSourceEquivalentTo(expectedOutput);
353354
}
354355

356+
// Tests that type annotations are correctly copied from the bounds of type parameters in the
357+
// @AutoValue class to the bounds of the corresponding parameters in the generated class. For
358+
// example, if we have `@AutoValue abstract class Foo<T extends @NullableType Object>`, then the
359+
// generated class should be `class AutoValue_Foo<T extends @NullableType Object> extends Foo<T>`.
360+
// Some buggy versions of javac do not report type annotations correctly in this context.
361+
// AutoValue can't copy them if it can't see them, so we make a special annotation processor to
362+
// detect if we are in the presence of this bug and if so we don't fail.
363+
@Test
364+
public void testTypeParametersWithAnnotationsOnBounds() {
365+
@SupportedAnnotationTypes("*")
366+
class CompilerBugProcessor extends AbstractProcessor {
367+
boolean checkedAnnotationsOnTypeBounds;
368+
boolean reportsAnnotationsOnTypeBounds;
369+
370+
@Override
371+
public SourceVersion getSupportedSourceVersion() {
372+
return SourceVersion.latestSupported();
373+
}
374+
375+
@Override
376+
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
377+
if (roundEnv.processingOver()) {
378+
TypeElement test = processingEnv.getElementUtils().getTypeElement("com.example.Test");
379+
TypeParameterElement t = test.getTypeParameters().get(0);
380+
this.checkedAnnotationsOnTypeBounds = true;
381+
this.reportsAnnotationsOnTypeBounds =
382+
!t.getBounds().get(0).getAnnotationMirrors().isEmpty();
383+
}
384+
return false;
385+
}
386+
}
387+
CompilerBugProcessor compilerBugProcessor = new CompilerBugProcessor();
388+
JavaFileObject nullableTypeFileObject =
389+
JavaFileObjects.forSourceLines(
390+
"foo.bar.NullableType",
391+
"package foo.bar;",
392+
"",
393+
"import java.lang.annotation.ElementType;",
394+
"import java.lang.annotation.Target;",
395+
"",
396+
"@Target(ElementType.TYPE_USE)",
397+
"public @interface NullableType {}");
398+
JavaFileObject autoValueFileObject =
399+
JavaFileObjects.forSourceLines(
400+
"com.example.Test",
401+
"package com.example;",
402+
"",
403+
"import com.google.auto.value.AutoValue;",
404+
"import foo.bar.NullableType;",
405+
"",
406+
"@AutoValue",
407+
"abstract class Test<T extends @NullableType Object & @NullableType Cloneable> {}");
408+
Compilation compilation =
409+
javac()
410+
.withProcessors(new AutoValueProcessor(), compilerBugProcessor)
411+
.withOptions("-Xlint:-processing", "-implicit:none")
412+
.compile(nullableTypeFileObject, autoValueFileObject);
413+
assertThat(compilation).succeededWithoutWarnings();
414+
assertThat(compilerBugProcessor.checkedAnnotationsOnTypeBounds).isTrue();
415+
if (compilerBugProcessor.reportsAnnotationsOnTypeBounds) {
416+
assertThat(compilation)
417+
.generatedSourceFile("com.example.AutoValue_Test")
418+
.contentsAsUtf8String()
419+
.contains(
420+
"class AutoValue_Test<T extends @NullableType Object & @NullableType Cloneable>"
421+
+ " extends Test<T> {");
422+
}
423+
}
424+
355425
// In the following few tests, see AutoValueProcessor.validateMethods for why unrecognized
356426
// abstract methods provoke only a warning rather than an error. Compilation will fail anyway
357427
// because the generated class is not abstract and does not implement the unrecognized methods.
@@ -2538,12 +2608,13 @@ public void autoValueBuilderToBuilderWrongTypeParameters() {
25382608
" Baz<K, V> build();",
25392609
" }",
25402610
"}");
2541-
assertAbout(javaSource())
2542-
.that(javaFileObject)
2543-
.processedWith(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2544-
.failsToCompile()
2545-
.withErrorContaining("Builder converter method should return foo.bar.Baz.Builder<K, V>")
2546-
.in(javaFileObject)
2611+
Compilation compilation =
2612+
javac()
2613+
.withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2614+
.compile(javaFileObject);
2615+
assertThat(compilation)
2616+
.hadErrorContaining("Builder converter method should return foo.bar.Baz.Builder<K, V>")
2617+
.inFile(javaFileObject)
25472618
.onLine(9);
25482619
}
25492620

0 commit comments

Comments
 (0)