xref: /aosp_15_r20/external/auto/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java (revision 1c2bbba85eccddce6de79cbbf1645fda32e723f0)
1 /*
2  * Copyright 2018 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.auto.value.processor;
17 
18 import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION;
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.truth.TruthJUnit.assume;
21 import static com.google.testing.compile.CompilationSubject.assertThat;
22 import static com.google.testing.compile.CompilationSubject.compilations;
23 import static com.google.testing.compile.Compiler.javac;
24 import static java.util.Arrays.stream;
25 import static java.util.stream.Collectors.joining;
26 
27 import com.google.common.collect.ImmutableList;
28 import com.google.common.collect.ImmutableMap;
29 import com.google.common.collect.ImmutableSet;
30 import com.google.common.truth.Expect;
31 import com.google.testing.compile.Compilation;
32 import com.google.testing.compile.JavaFileObjects;
33 import java.io.IOException;
34 import java.io.PrintWriter;
35 import java.io.UncheckedIOException;
36 import java.io.Writer;
37 import java.lang.annotation.Retention;
38 import java.lang.annotation.RetentionPolicy;
39 import java.util.Set;
40 import javax.annotation.processing.AbstractProcessor;
41 import javax.annotation.processing.RoundEnvironment;
42 import javax.annotation.processing.SupportedAnnotationTypes;
43 import javax.lang.model.SourceVersion;
44 import javax.lang.model.element.Element;
45 import javax.lang.model.element.TypeElement;
46 import javax.lang.model.element.TypeParameterElement;
47 import javax.lang.model.util.ElementFilter;
48 import javax.tools.JavaFileObject;
49 import org.junit.Rule;
50 import org.junit.Test;
51 import org.junit.runner.RunWith;
52 import org.junit.runners.JUnit4;
53 
54 /** @author [email protected] (Éamonn McManus) */
55 @RunWith(JUnit4.class)
56 public class AutoValueCompilationTest {
57   @Rule public final Expect expect = Expect.create();
58 
59   // Sadly we can't rely on JDK 8 to handle type annotations correctly.
60   // Some versions do, some don't. So skip the test unless we are on at least JDK 9.
61   private boolean typeAnnotationsWork =
62       Double.parseDouble(JAVA_SPECIFICATION_VERSION.value()) >= 9.0;
63 
64   @Test
simpleSuccess()65   public void simpleSuccess() {
66     // Positive test case that ensures we generate the expected code for at least one case.
67     // Most AutoValue code-generation tests are functional, meaning that we check that the generated
68     // code does the right thing rather than checking what it looks like, but this test checks that
69     // we are not generating correct but weird code.
70     JavaFileObject javaFileObject =
71         JavaFileObjects.forSourceLines(
72             "foo.bar.Baz",
73             "package foo.bar;",
74             "",
75             "import com.google.auto.value.AutoValue;",
76             "",
77             "@AutoValue",
78             "public abstract class Baz {",
79             "  public abstract long buh();",
80             "",
81             "  public static Baz create(long buh) {",
82             "    return new AutoValue_Baz(buh);",
83             "  }",
84             "}");
85     JavaFileObject expectedOutput =
86         JavaFileObjects.forSourceLines(
87             "foo.bar.AutoValue_Baz",
88             "package foo.bar;",
89             "",
90             GeneratedImport.importGeneratedAnnotationType(),
91             "",
92             "@Generated(\"" + AutoValueProcessor.class.getName() + "\")",
93             "final class AutoValue_Baz extends Baz {",
94             "  private final long buh;",
95             "",
96             "  AutoValue_Baz(long buh) {",
97             "    this.buh = buh;",
98             "  }",
99             "",
100             "  @Override public long buh() {",
101             "    return buh;",
102             "  }",
103             "",
104             "  @Override public String toString() {",
105             "    return \"Baz{\"",
106             "        + \"buh=\" + buh",
107             "        + \"}\";",
108             "  }",
109             "",
110             "  @Override public boolean equals(Object o) {",
111             "    if (o == this) {",
112             "      return true;",
113             "    }",
114             "    if (o instanceof Baz) {",
115             "      Baz that = (Baz) o;",
116             "      return this.buh == that.buh();",
117             "    }",
118             "    return false;",
119             "  }",
120             "",
121             "  @Override public int hashCode() {",
122             "    int h$ = 1;",
123             "    h$ *= 1000003;",
124             "    h$ ^= (int) ((buh >>> 32) ^ buh);",
125             "    return h$;",
126             "  }",
127             "}");
128     Compilation compilation =
129         javac()
130             .withProcessors(new AutoValueProcessor())
131             .withOptions("-A" + Nullables.NULLABLE_OPTION + "=")
132             .compile(javaFileObject);
133     assertThat(compilation)
134         .generatedSourceFile("foo.bar.AutoValue_Baz")
135         .hasSourceEquivalentTo(expectedOutput);
136   }
137 
138   @Test
importTwoWays()139   public void importTwoWays() {
140     // Test that referring to the same class in two different ways does not confuse the import logic
141     // into thinking it is two different classes and that therefore it can't import. The code here
142     // is nonsensical but successfully reproduces a real problem, which is that a TypeMirror that is
143     // extracted using Elements.getTypeElement(name).asType() does not compare equal to one that is
144     // extracted from ExecutableElement.getReturnType(), even though Types.isSameType considers them
145     // equal. So unless we are careful, the java.util.Arrays that we import explicitly to use its
146     // methods will appear different from the java.util.Arrays that is the return type of the
147     // arrays() method here.
148     JavaFileObject javaFileObject =
149         JavaFileObjects.forSourceLines(
150             "foo.bar.Baz",
151             "package foo.bar;",
152             "",
153             "import com.google.auto.value.AutoValue;",
154             "",
155             "import java.util.Arrays;",
156             "",
157             "@AutoValue",
158             "public abstract class Baz {",
159             "  @SuppressWarnings(\"mutable\")",
160             "  public abstract int[] ints();",
161             "  public abstract Arrays arrays();",
162             "",
163             "  public static Baz create(int[] ints, Arrays arrays) {",
164             "    return new AutoValue_Baz(ints, arrays);",
165             "  }",
166             "}");
167     JavaFileObject expectedOutput =
168         JavaFileObjects.forSourceLines(
169             "foo.bar.AutoValue_Baz",
170             "package foo.bar;",
171             "",
172             "import java.util.Arrays;",
173             GeneratedImport.importGeneratedAnnotationType(),
174             "",
175             "@Generated(\"" + AutoValueProcessor.class.getName() + "\")",
176             "final class AutoValue_Baz extends Baz {",
177             "  private final int[] ints;",
178             "  private final Arrays arrays;",
179             "",
180             "  AutoValue_Baz(int[] ints, Arrays arrays) {",
181             "    if (ints == null) {",
182             "      throw new NullPointerException(\"Null ints\");",
183             "    }",
184             "    this.ints = ints;",
185             "    if (arrays == null) {",
186             "      throw new NullPointerException(\"Null arrays\");",
187             "    }",
188             "    this.arrays = arrays;",
189             "  }",
190             "",
191             "  @SuppressWarnings(\"mutable\")",
192             "  @Override public int[] ints() {",
193             "    return ints;",
194             "  }",
195             "",
196             "  @Override public Arrays arrays() {",
197             "    return arrays;",
198             "  }",
199             "",
200             "  @Override public String toString() {",
201             "    return \"Baz{\"",
202             "        + \"ints=\" + Arrays.toString(ints) + \", \"",
203             "        + \"arrays=\" + arrays",
204             "        + \"}\";",
205             "  }",
206             "",
207             "  @Override public boolean equals(Object o) {",
208             "    if (o == this) {",
209             "      return true;",
210             "    }",
211             "    if (o instanceof Baz) {",
212             "      Baz that = (Baz) o;",
213             "      return Arrays.equals(this.ints, (that instanceof AutoValue_Baz) "
214                 + "? ((AutoValue_Baz) that).ints : that.ints())",
215             "          && this.arrays.equals(that.arrays());",
216             "    }",
217             "    return false;",
218             "  }",
219             "",
220             "  @Override public int hashCode() {",
221             "    int h$ = 1;",
222             "    h$ *= 1000003;",
223             "    h$ ^= Arrays.hashCode(ints);",
224             "    h$ *= 1000003;",
225             "    h$ ^= arrays.hashCode();",
226             "    return h$;",
227             "  }",
228             "}");
229     Compilation compilation =
230         javac()
231             .withProcessors(new AutoValueProcessor())
232             .withOptions("-A" + Nullables.NULLABLE_OPTION + "=")
233             .compile(javaFileObject);
234     assertThat(compilation)
235         .generatedSourceFile("foo.bar.AutoValue_Baz")
236         .hasSourceEquivalentTo(expectedOutput);
237   }
238 
239   @Test
testNoWarningsFromGenerics()240   public void testNoWarningsFromGenerics() {
241     JavaFileObject javaFileObject =
242         JavaFileObjects.forSourceLines(
243             "foo.bar.Baz",
244             "package foo.bar;",
245             "import com.google.auto.value.AutoValue;",
246             "@AutoValue",
247             "public abstract class Baz<T extends Number, U extends T> {",
248             "  public abstract T t();",
249             "  public abstract U u();",
250             "  public static <T extends Number, U extends T> Baz<T, U> create(T t, U u) {",
251             "    return new AutoValue_Baz<T, U>(t, u);",
252             "  }",
253             "}");
254     Compilation compilation =
255         javac()
256             .withProcessors(new AutoValueProcessor())
257             .withOptions("-Xlint:-processing", "-implicit:none")
258             .compile(javaFileObject);
259     assertThat(compilation).succeededWithoutWarnings();
260   }
261 
262   @Test
testNestedParameterizedTypesWithTypeAnnotations()263   public void testNestedParameterizedTypesWithTypeAnnotations() {
264     assume().that(typeAnnotationsWork).isTrue();
265     JavaFileObject annotFileObject =
266         JavaFileObjects.forSourceLines(
267             "foo.bar.Annot",
268             "package foo.bar;",
269             "",
270             "import java.lang.annotation.ElementType;",
271             "import java.lang.annotation.Target;",
272             "",
273             "@Target(ElementType.TYPE_USE)",
274             "public @interface Annot {",
275             "  int value();",
276             "}");
277     JavaFileObject outerFileObject =
278         JavaFileObjects.forSourceLines(
279             "foo.baz.OuterWithTypeParam",
280             "package foo.baz;",
281             "",
282             "public class OuterWithTypeParam<T extends Number> {",
283             "  public class InnerWithTypeParam<U> {}",
284             "}");
285     JavaFileObject nestyFileObject =
286         JavaFileObjects.forSourceLines(
287             "com.example.Nesty",
288             "package com.example;",
289             "",
290             "import com.google.auto.value.AutoValue;",
291             "import foo.bar.Annot;",
292             "import foo.baz.OuterWithTypeParam;",
293             "",
294             "@AutoValue",
295             "abstract class Nesty {",
296             "  abstract @Annot(1) OuterWithTypeParam<@Annot(2) Double>",
297             "      .@Annot(3) InnerWithTypeParam<@Annot(4) String> inner();",
298             "",
299             "  static Nesty of(",
300             "      @Annot(1) OuterWithTypeParam<@Annot(2) Double>",
301             "          .@Annot(3) InnerWithTypeParam<@Annot(4) String> inner) {",
302             "    return new AutoValue_Nesty(inner);",
303             "  }",
304             "}");
305     JavaFileObject expectedOutput =
306         JavaFileObjects.forSourceLines(
307             "com.example.AutoValue_Nesty",
308             "package com.example;",
309             "",
310             "import foo.bar.Annot;",
311             "import foo.baz.OuterWithTypeParam;",
312             GeneratedImport.importGeneratedAnnotationType(),
313             "",
314             "@Generated(\"com.google.auto.value.processor.AutoValueProcessor\")",
315             "final class AutoValue_Nesty extends Nesty {",
316             "  private final @Annot(1) OuterWithTypeParam<@Annot(2) Double>"
317                 + ".@Annot(3) InnerWithTypeParam<@Annot(4) String> inner;",
318             "",
319             "  AutoValue_Nesty(",
320             "      @Annot(1) OuterWithTypeParam<@Annot(2) Double>"
321                 + ".@Annot(3) InnerWithTypeParam<@Annot(4) String> inner) {",
322             "    if (inner == null) {",
323             "      throw new NullPointerException(\"Null inner\");",
324             "    }",
325             "    this.inner = inner;",
326             "  }",
327             "",
328             "  @Override",
329             "  @Annot(1) OuterWithTypeParam<@Annot(2) Double>"
330                 + ".@Annot(3) InnerWithTypeParam<@Annot(4) String> inner() {",
331             "    return inner;",
332             "  }",
333             "",
334             "  @Override",
335             "  public String toString() {",
336             "    return \"Nesty{\"",
337             "        + \"inner=\" + inner",
338             "        + \"}\";",
339             "  }",
340             "",
341             "  @Override",
342             "  public boolean equals(Object o) {",
343             "    if (o == this) {",
344             "      return true;",
345             "    }",
346             "    if (o instanceof Nesty) {",
347             "      Nesty that = (Nesty) o;",
348             "      return this.inner.equals(that.inner());",
349             "    }",
350             "    return false;",
351             "  }",
352             "",
353             "  @Override",
354             "  public int hashCode() {",
355             "    int h$ = 1;",
356             "    h$ *= 1000003;",
357             "    h$ ^= inner.hashCode();",
358             "    return h$;",
359             "  }",
360             "}");
361 
362     Compilation compilation =
363         javac()
364             .withProcessors(new AutoValueProcessor())
365             .withOptions(
366                 "-Xlint:-processing", "-implicit:none", "-A" + Nullables.NULLABLE_OPTION + "=")
367             .compile(annotFileObject, outerFileObject, nestyFileObject);
368     assertThat(compilation).succeededWithoutWarnings();
369     assertThat(compilation)
370         .generatedSourceFile("com.example.AutoValue_Nesty")
371         .hasSourceEquivalentTo(expectedOutput);
372   }
373 
374   // Tests that type annotations are correctly copied from the bounds of type parameters in the
375   // @AutoValue class to the bounds of the corresponding parameters in the generated class. For
376   // example, if we have `@AutoValue abstract class Foo<T extends @NullableType Object>`, then the
377   // generated class should be `class AutoValue_Foo<T extends @NullableType Object> extends Foo<T>`.
378   // Some buggy versions of javac do not report type annotations correctly in this context.
379   // AutoValue can't copy them if it can't see them, so we make a special annotation processor to
380   // detect if we are in the presence of this bug and if so we don't fail.
381   @Test
testTypeParametersWithAnnotationsOnBounds()382   public void testTypeParametersWithAnnotationsOnBounds() {
383     @SupportedAnnotationTypes("*")
384     class CompilerBugProcessor extends AbstractProcessor {
385       boolean checkedAnnotationsOnTypeBounds;
386       boolean reportsAnnotationsOnTypeBounds;
387 
388       @Override
389       public SourceVersion getSupportedSourceVersion() {
390         return SourceVersion.latestSupported();
391       }
392 
393       @Override
394       public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
395         if (roundEnv.processingOver()) {
396           TypeElement test = processingEnv.getElementUtils().getTypeElement("com.example.Test");
397           TypeParameterElement t = test.getTypeParameters().get(0);
398           this.checkedAnnotationsOnTypeBounds = true;
399           this.reportsAnnotationsOnTypeBounds =
400               !t.getBounds().get(0).getAnnotationMirrors().isEmpty();
401         }
402         return false;
403       }
404     }
405     CompilerBugProcessor compilerBugProcessor = new CompilerBugProcessor();
406     JavaFileObject nullableTypeFileObject =
407         JavaFileObjects.forSourceLines(
408             "foo.bar.NullableType",
409             "package foo.bar;",
410             "",
411             "import java.lang.annotation.ElementType;",
412             "import java.lang.annotation.Target;",
413             "",
414             "@Target(ElementType.TYPE_USE)",
415             "public @interface NullableType {}");
416     JavaFileObject autoValueFileObject =
417         JavaFileObjects.forSourceLines(
418             "com.example.Test",
419             "package com.example;",
420             "",
421             "import com.google.auto.value.AutoValue;",
422             "import foo.bar.NullableType;",
423             "",
424             "@AutoValue",
425             "abstract class Test<T extends @NullableType Object & @NullableType Cloneable> {}");
426     Compilation compilation =
427         javac()
428             .withProcessors(new AutoValueProcessor(), compilerBugProcessor)
429             .withOptions("-Xlint:-processing", "-implicit:none")
430             .compile(nullableTypeFileObject, autoValueFileObject);
431     assertThat(compilation).succeededWithoutWarnings();
432     assertThat(compilerBugProcessor.checkedAnnotationsOnTypeBounds).isTrue();
433     if (compilerBugProcessor.reportsAnnotationsOnTypeBounds) {
434       assertThat(compilation)
435           .generatedSourceFile("com.example.AutoValue_Test")
436           .contentsAsUtf8String()
437           .contains(
438               "class AutoValue_Test<T extends @NullableType Object & @NullableType Cloneable>"
439                   + " extends Test<T> {");
440     }
441   }
442 
443   // In the following few tests, see AutoValueProcessor.validateMethods for why unrecognized
444   // abstract methods provoke only a warning rather than an error. Compilation will fail anyway
445   // because the generated class is not abstract and does not implement the unrecognized methods.
446 
447   @Test
testAbstractVoid()448   public void testAbstractVoid() {
449     JavaFileObject javaFileObject =
450         JavaFileObjects.forSourceLines(
451             "foo.bar.Baz",
452             "package foo.bar;",
453             "import com.google.auto.value.AutoValue;",
454             "@AutoValue",
455             "public abstract class Baz {",
456             "  public abstract void foo();",
457             "}");
458     Compilation compilation =
459         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
460     assertThat(compilation).failed();
461     assertThat(compilation)
462         .hadWarningContaining(
463             "Abstract method is neither a property getter nor a Builder converter")
464         .inFile(javaFileObject)
465         .onLineContaining("void foo()");
466   }
467 
468   @Test
testAbstractWithParams()469   public void testAbstractWithParams() {
470     JavaFileObject javaFileObject =
471         JavaFileObjects.forSourceLines(
472             "foo.bar.Baz",
473             "package foo.bar;",
474             "import com.google.auto.value.AutoValue;",
475             "@AutoValue",
476             "public abstract class Baz {",
477             "  public abstract int foo(int bar);",
478             "}");
479     Compilation compilation =
480         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
481     assertThat(compilation).failed();
482     assertThat(compilation)
483         .hadWarningContaining(
484             "Abstract method is neither a property getter nor a Builder converter")
485         .inFile(javaFileObject)
486         .onLineContaining("int foo(int bar)");
487   }
488 
489   @Test
testPrimitiveArrayWarning()490   public void testPrimitiveArrayWarning() {
491     JavaFileObject javaFileObject =
492         JavaFileObjects.forSourceLines(
493             "foo.bar.Baz",
494             "package foo.bar;",
495             "import com.google.auto.value.AutoValue;",
496             "@AutoValue",
497             "public abstract class Baz {",
498             "  public abstract byte[] bytes();",
499             "  public static Baz create(byte[] bytes) {",
500             "    return new AutoValue_Baz(bytes);",
501             "  }",
502             "}");
503     Compilation compilation =
504         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
505     assertThat(compilation).succeeded();
506     assertThat(compilation)
507         .hadWarningContaining(
508             "An @AutoValue property that is a primitive array returns the original array")
509         .inFile(javaFileObject)
510         .onLineContaining("byte[] bytes()");
511   }
512 
513   @Test
testPrimitiveArrayWarningFromParent()514   public void testPrimitiveArrayWarningFromParent() {
515     // If the array-valued property is defined by an ancestor then we shouldn't try to attach
516     // the warning to the method that defined it, but rather to the @AutoValue class itself.
517     JavaFileObject javaFileObject =
518         JavaFileObjects.forSourceLines(
519             "foo.bar.Baz",
520             "package foo.bar;",
521             "import com.google.auto.value.AutoValue;",
522             "public abstract class Baz {",
523             "  public abstract byte[] bytes();",
524             "",
525             "  @AutoValue",
526             "  public abstract static class BazChild extends Baz {",
527             "    public static BazChild create(byte[] bytes) {",
528             "      return new AutoValue_Baz_BazChild(bytes);",
529             "    }",
530             "  }",
531             "}");
532     Compilation compilation =
533         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
534     assertThat(compilation).succeeded();
535     assertThat(compilation)
536         .hadWarningContainingMatch(
537             "An @AutoValue property that is a primitive array returns the original array"
538                 + ".*foo\\.bar\\.Baz\\.bytes")
539         .inFile(javaFileObject)
540         .onLineContaining("BazChild extends Baz");
541   }
542 
543   @Test
testPrimitiveArrayWarningSuppressed()544   public void testPrimitiveArrayWarningSuppressed() {
545     JavaFileObject javaFileObject =
546         JavaFileObjects.forSourceLines(
547             "foo.bar.Baz",
548             "package foo.bar;",
549             "import com.google.auto.value.AutoValue;",
550             "@AutoValue",
551             "public abstract class Baz {",
552             "  @SuppressWarnings(\"mutable\")",
553             "  public abstract byte[] bytes();",
554             "  public static Baz create(byte[] bytes) {",
555             "    return new AutoValue_Baz(bytes);",
556             "  }",
557             "}");
558     Compilation compilation =
559         javac()
560             .withProcessors(new AutoValueProcessor())
561             .withOptions("-Xlint:-processing", "-implicit:none")
562             .compile(javaFileObject);
563     assertThat(compilation).succeededWithoutWarnings();
564   }
565 
566   @Test
autoValueMustBeClass()567   public void autoValueMustBeClass() {
568     JavaFileObject javaFileObject =
569         JavaFileObjects.forSourceLines(
570             "foo.bar.Baz",
571             "package foo.bar;",
572             "",
573             "import com.google.auto.value.AutoValue;",
574             "",
575             "@AutoValue",
576             "public interface Baz {",
577             "  String buh();",
578             "}");
579     Compilation compilation =
580         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
581     assertThat(compilation)
582         .hadErrorContaining("@AutoValue only applies to classes")
583         .inFile(javaFileObject)
584         .onLineContaining("interface Baz");
585   }
586 
587   @Test
autoValueMustNotBeFinal()588   public void autoValueMustNotBeFinal() {
589     JavaFileObject javaFileObject =
590         JavaFileObjects.forSourceLines(
591             "foo.bar.Baz",
592             "package foo.bar;",
593             "",
594             "import com.google.auto.value.AutoValue;",
595             "",
596             "@AutoValue",
597             "public final class Baz {",
598             "  public Baz create() {",
599             "    return new AutoValue_Baz();",
600             "  }",
601             "}");
602     Compilation compilation =
603         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
604     assertThat(compilation)
605         .hadErrorContaining("@AutoValue class must not be final")
606         .inFile(javaFileObject)
607         .onLineContaining("class Baz");
608   }
609 
610   @Test
autoValueMustBeStatic()611   public void autoValueMustBeStatic() {
612     JavaFileObject javaFileObject =
613         JavaFileObjects.forSourceLines(
614             "foo.bar.Baz",
615             "package foo.bar;",
616             "",
617             "import com.google.auto.value.AutoValue;",
618             "",
619             "public class Baz {",
620             "  @AutoValue",
621             "  public abstract class NotStatic {",
622             "    public abstract String buh();",
623             "    public NotStatic create(String buh) {",
624             "      return new AutoValue_Baz_NotStatic(buh);",
625             "    }",
626             "  }",
627             "}");
628     Compilation compilation =
629         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
630     assertThat(compilation)
631         .hadErrorContaining("Nested @AutoValue class must be static")
632         .inFile(javaFileObject)
633         .onLineContaining("abstract class NotStatic");
634   }
635 
636   @Test
autoValueMustNotBePrivate()637   public void autoValueMustNotBePrivate() {
638     JavaFileObject javaFileObject =
639         JavaFileObjects.forSourceLines(
640             "foo.bar.Baz",
641             "package foo.bar;",
642             "",
643             "import com.google.auto.value.AutoValue;",
644             "",
645             "public class Baz {",
646             "  @AutoValue",
647             "  private abstract static class Private {",
648             "    public abstract String buh();",
649             "    public Private create(String buh) {",
650             "      return new AutoValue_Baz_Private(buh);",
651             "    }",
652             "  }",
653             "}");
654     Compilation compilation =
655         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
656     assertThat(compilation)
657         .hadErrorContaining("@AutoValue class must not be private")
658         .inFile(javaFileObject)
659         .onLineContaining("class Private");
660   }
661 
662   @Test
autoValueMustBeNotBeNestedInPrivate()663   public void autoValueMustBeNotBeNestedInPrivate() {
664     JavaFileObject javaFileObject =
665         JavaFileObjects.forSourceLines(
666             "foo.bar.Baz",
667             "package foo.bar;",
668             "",
669             "import com.google.auto.value.AutoValue;",
670             "",
671             "public class Baz {",
672             "  private static class Private {",
673             "    @AutoValue",
674             "    abstract static class Nested {",
675             "      public abstract String buh();",
676             "      public Nested create(String buh) {",
677             "        return new AutoValue_Baz_Private_Nested(buh);",
678             "      }",
679             "    }",
680             "  }",
681             "}");
682     Compilation compilation =
683         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
684     assertThat(compilation)
685         .hadErrorContaining("@AutoValue class must not be nested in a private class")
686         .inFile(javaFileObject)
687         .onLineContaining("class Nested");
688   }
689 
690   @Test
autoValueMustHaveNoArgConstructor()691   public void autoValueMustHaveNoArgConstructor() {
692     JavaFileObject javaFileObject =
693         JavaFileObjects.forSourceLines(
694             "foo.bar.Baz",
695             "package foo.bar;",
696             "",
697             "import com.google.auto.value.AutoValue;",
698             "",
699             "@AutoValue",
700             "public abstract class Baz {",
701             "  Baz(int buh) {}",
702             "",
703             "  public abstract int buh();",
704             "}");
705     Compilation compilation =
706         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
707     assertThat(compilation)
708         .hadErrorContaining("@AutoValue class must have a non-private no-arg constructor")
709         .inFile(javaFileObject)
710         .onLineContaining("class Baz");
711   }
712 
713   @Test
autoValueMustHaveVisibleNoArgConstructor()714   public void autoValueMustHaveVisibleNoArgConstructor() {
715     JavaFileObject javaFileObject =
716         JavaFileObjects.forSourceLines(
717             "foo.bar.Baz",
718             "package foo.bar;",
719             "",
720             "import com.google.auto.value.AutoValue;",
721             "",
722             "@AutoValue",
723             "public abstract class Baz {",
724             "  private Baz() {}",
725             "",
726             "  public abstract int buh();",
727             "}");
728     Compilation compilation =
729         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
730     assertThat(compilation)
731         .hadErrorContaining("@AutoValue class must have a non-private no-arg constructor")
732         .inFile(javaFileObject)
733         .onLineContaining("class Baz");
734   }
735 
736   @Test
noMultidimensionalPrimitiveArrays()737   public void noMultidimensionalPrimitiveArrays() {
738     JavaFileObject javaFileObject =
739         JavaFileObjects.forSourceLines(
740             "foo.bar.Baz",
741             "package foo.bar;",
742             "",
743             "import com.google.auto.value.AutoValue;",
744             "",
745             "@AutoValue",
746             "public abstract class Baz {",
747             "  public abstract int[][] ints();",
748             "",
749             "  public static Baz create(int[][] ints) {",
750             "    return new AutoValue_Baz(ints);",
751             "  }",
752             "}");
753     Compilation compilation =
754         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
755     assertThat(compilation)
756         .hadErrorContaining(
757             "@AutoValue class cannot define an array-valued property "
758                 + "unless it is a primitive array")
759         .inFile(javaFileObject)
760         .onLineContaining("int[][] ints()");
761   }
762 
763   @Test
noObjectArrays()764   public void noObjectArrays() {
765     JavaFileObject javaFileObject =
766         JavaFileObjects.forSourceLines(
767             "foo.bar.Baz",
768             "package foo.bar;",
769             "",
770             "import com.google.auto.value.AutoValue;",
771             "",
772             "@AutoValue",
773             "public abstract class Baz {",
774             "  public abstract String[] strings();",
775             "",
776             "  public static Baz create(String[] strings) {",
777             "    return new AutoValue_Baz(strings);",
778             "  }",
779             "}");
780     Compilation compilation =
781         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
782     assertThat(compilation)
783         .hadErrorContaining(
784             "@AutoValue class cannot define an array-valued property "
785                 + "unless it is a primitive array")
786         .inFile(javaFileObject)
787         .onLineContaining("String[] strings()");
788   }
789 
790   @Test
annotationOnInterface()791   public void annotationOnInterface() {
792     JavaFileObject javaFileObject =
793         JavaFileObjects.forSourceLines(
794             "foo.bar.Baz",
795             "package foo.bar;",
796             "",
797             "import com.google.auto.value.AutoValue;",
798             "",
799             "@AutoValue",
800             "public interface Baz {}");
801     Compilation compilation =
802         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
803     assertThat(compilation)
804         .hadErrorContaining("AutoValue only applies to classes")
805         .inFile(javaFileObject)
806         .onLineContaining("interface Baz");
807   }
808 
809   @Test
annotationOnEnum()810   public void annotationOnEnum() {
811     JavaFileObject javaFileObject =
812         JavaFileObjects.forSourceLines(
813             "foo.bar.Baz",
814             "package foo.bar;",
815             "",
816             "import com.google.auto.value.AutoValue;",
817             "",
818             "@AutoValue",
819             "public enum Baz {}");
820     Compilation compilation =
821         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
822     assertThat(compilation)
823         .hadErrorContaining("AutoValue only applies to classes")
824         .inFile(javaFileObject)
825         .onLineContaining("enum Baz");
826   }
827 
828   @Test
extendAutoValue()829   public void extendAutoValue() {
830     JavaFileObject javaFileObject =
831         JavaFileObjects.forSourceLines(
832             "foo.bar.Outer",
833             "package foo.bar;",
834             "",
835             "import com.google.auto.value.AutoValue;",
836             "",
837             "public class Outer {",
838             "  @AutoValue",
839             "  static abstract class Parent {",
840             "    static Parent create(int randomProperty) {",
841             "      return new AutoValue_Outer_Parent(randomProperty);",
842             "    }",
843             "",
844             "    abstract int randomProperty();",
845             "  }",
846             "",
847             "  @AutoValue",
848             "  static abstract class Child extends Parent {",
849             "    static Child create(int randomProperty) {",
850             "      return new AutoValue_Outer_Child(randomProperty);",
851             "    }",
852             "",
853             "    abstract int randomProperty();",
854             "  }",
855             "}");
856     Compilation compilation =
857         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
858     assertThat(compilation)
859         .hadErrorContaining("may not extend")
860         .inFile(javaFileObject)
861         .onLineContaining("Child extends Parent");
862   }
863 
864   @Test
bogusSerialVersionUID()865   public void bogusSerialVersionUID() {
866     String[] mistakes = {
867       "final long serialVersionUID = 1234L", // not static
868       "static long serialVersionUID = 1234L", // not final
869       "static final Long serialVersionUID = 1234L", // not long
870       "static final long serialVersionUID = (Long) 1234L", // not a compile-time constant
871     };
872     for (String mistake : mistakes) {
873       JavaFileObject javaFileObject =
874           JavaFileObjects.forSourceLines(
875               "foo.bar.Baz",
876               "package foo.bar;",
877               "",
878               "import com.google.auto.value.AutoValue;",
879               "",
880               "@AutoValue",
881               "public abstract class Baz implements java.io.Serializable {",
882               "  " + mistake + ";",
883               "",
884               "  public abstract int foo();",
885               "}");
886       Compilation compilation =
887           javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
888       expect
889           .about(compilations())
890           .that(compilation)
891           .hadErrorContaining("serialVersionUID must be a static final long compile-time constant")
892           .inFile(javaFileObject)
893           .onLineContaining(mistake);
894     }
895   }
896 
897   @Test
nonExistentSuperclass()898   public void nonExistentSuperclass() {
899     // The main purpose of this test is to check that AutoValueProcessor doesn't crash the
900     // compiler in this case.
901     JavaFileObject javaFileObject =
902         JavaFileObjects.forSourceLines(
903             "foo.bar.Baz",
904             "package foo.bar;",
905             "",
906             "import com.google.auto.value.AutoValue;",
907             "",
908             "@AutoValue",
909             "public abstract class Existent extends NonExistent {",
910             "}");
911     Compilation compilation =
912         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
913     assertThat(compilation)
914         .hadErrorContaining("NonExistent")
915         .inFile(javaFileObject)
916         .onLineContaining("NonExistent");
917   }
918 
919   @Test
cannotImplementAnnotation()920   public void cannotImplementAnnotation() {
921     JavaFileObject javaFileObject =
922         JavaFileObjects.forSourceLines(
923             "foo.bar.RetentionImpl",
924             "package foo.bar;",
925             "",
926             "import com.google.auto.value.AutoValue;",
927             "import java.lang.annotation.Retention;",
928             "import java.lang.annotation.RetentionPolicy;",
929             "",
930             "@AutoValue",
931             "public abstract class RetentionImpl implements Retention {",
932             "  public static Retention create(RetentionPolicy policy) {",
933             "    return new AutoValue_RetentionImpl(policy);",
934             "  }",
935             "",
936             "  @Override public Class<? extends Retention> annotationType() {",
937             "    return Retention.class;",
938             "  }",
939             "",
940             "  @Override public boolean equals(Object o) {",
941             "    return (o instanceof Retention && value().equals((Retention) o).value());",
942             "  }",
943             "",
944             "  @Override public int hashCode() {",
945             "    return (\"value\".hashCode() * 127) ^ value().hashCode();",
946             "  }",
947             "}");
948     Compilation compilation =
949         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
950     assertThat(compilation)
951         .hadErrorContaining("may not be used to implement an annotation interface")
952         .inFile(javaFileObject)
953         .onLineContaining("RetentionImpl implements Retention");
954   }
955 
956   @Test
missingPropertyType()957   public void missingPropertyType() {
958     JavaFileObject javaFileObject =
959         JavaFileObjects.forSourceLines(
960             "foo.bar.Baz",
961             "package foo.bar;",
962             "",
963             "import com.google.auto.value.AutoValue;",
964             "",
965             "@AutoValue",
966             "public abstract class Baz {",
967             "  public abstract MissingType missingType();",
968             "}");
969     Compilation compilation =
970         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
971     assertThat(compilation)
972         .hadErrorContaining("MissingType")
973         .inFile(javaFileObject)
974         .onLineContaining("MissingType");
975     assertThat(compilation)
976         .hadErrorContaining("references undefined types including MissingType");
977   }
978 
979   @Test
missingGenericPropertyType()980   public void missingGenericPropertyType() {
981     JavaFileObject javaFileObject =
982         JavaFileObjects.forSourceLines(
983             "foo.bar.Baz",
984             "package foo.bar;",
985             "",
986             "import com.google.auto.value.AutoValue;",
987             "",
988             "@AutoValue",
989             "public abstract class Baz {",
990             "  public abstract MissingType<?> missingType();",
991             "}");
992     Compilation compilation =
993         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
994     assertThat(compilation)
995         .hadErrorContaining("MissingType")
996         .inFile(javaFileObject)
997         .onLineContaining("MissingType");
998   }
999 
1000   @Test
missingComplexGenericPropertyType()1001   public void missingComplexGenericPropertyType() {
1002     JavaFileObject javaFileObject =
1003         JavaFileObjects.forSourceLines(
1004             "foo.bar.Baz",
1005             "package foo.bar;",
1006             "",
1007             "import com.google.auto.value.AutoValue;",
1008             "",
1009             "import java.util.Map;",
1010             "import java.util.Set;",
1011             "",
1012             "@AutoValue",
1013             "public abstract class Baz {",
1014             "  public abstract Map<Set<?>, MissingType<?>> missingType();",
1015             "}");
1016     Compilation compilation =
1017         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
1018     assertThat(compilation)
1019         .hadErrorContaining("MissingType")
1020         .inFile(javaFileObject)
1021         .onLineContaining("MissingType");
1022   }
1023 
1024   @Test
missingSuperclassGenericParameter()1025   public void missingSuperclassGenericParameter() {
1026     JavaFileObject javaFileObject =
1027         JavaFileObjects.forSourceLines(
1028             "foo.bar.Baz",
1029             "package foo.bar;",
1030             "",
1031             "import com.google.auto.value.AutoValue;",
1032             "",
1033             "@AutoValue",
1034             "public abstract class Baz<T extends MissingType<?>> {",
1035             "  public abstract int foo();",
1036             "}");
1037     Compilation compilation =
1038         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
1039     assertThat(compilation)
1040         .hadErrorContaining("MissingType")
1041         .inFile(javaFileObject)
1042         .onLineContaining("MissingType");
1043   }
1044 
1045   @Test
nullablePrimitive()1046   public void nullablePrimitive() {
1047     JavaFileObject javaFileObject =
1048         JavaFileObjects.forSourceLines(
1049             "foo.bar.Baz",
1050             "package foo.bar;",
1051             "",
1052             "import com.google.auto.value.AutoValue;",
1053             "",
1054             "@AutoValue",
1055             "public abstract class Baz {",
1056             "  @interface Nullable {}",
1057             "  public abstract @Nullable int foo();",
1058             "}");
1059     Compilation compilation =
1060         javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
1061     assertThat(compilation)
1062         .hadErrorContaining("Primitive types cannot be @Nullable")
1063         .inFile(javaFileObject)
1064         .onLineContaining("@Nullable int");
1065   }
1066 
1067   @Test
correctBuilder()1068   public void correctBuilder() {
1069     JavaFileObject javaFileObject =
1070         JavaFileObjects.forSourceLines(
1071             "foo.bar.Baz",
1072             "package foo.bar;",
1073             "",
1074             "import com.google.auto.value.AutoValue;",
1075             "import com.google.common.base.Optional;",
1076             "import com.google.common.collect.ImmutableMap;",
1077             "",
1078             "import java.util.ArrayList;",
1079             "import java.util.List;",
1080             "import java.util.Map;",
1081             "import javax.annotation.Nullable;",
1082             "",
1083             "@AutoValue",
1084             "public abstract class Baz<T extends Number> {",
1085             "  public abstract int anInt();",
1086             "  @SuppressWarnings(\"mutable\")",
1087             "  public abstract byte[] aByteArray();",
1088             "  @SuppressWarnings(\"mutable\")",
1089             "  @Nullable public abstract int[] aNullableIntArray();",
1090             "  public abstract List<T> aList();",
1091             "  public abstract ImmutableMap<T, String> anImmutableMap();",
1092             "  public abstract Optional<String> anOptionalString();",
1093             "  public abstract NestedAutoValue<T> aNestedAutoValue();",
1094             "",
1095             "  public abstract Builder<T> toBuilder();",
1096             "",
1097             "  @AutoValue.Builder",
1098             "  public abstract static class Builder<T extends Number> {",
1099             "    public abstract Builder<T> anInt(int x);",
1100             "    public abstract Builder<T> aByteArray(byte[] x);",
1101             "    public abstract Builder<T> aNullableIntArray(@Nullable int[] x);",
1102             "    public abstract Builder<T> aList(List<T> x);",
1103             "    public abstract Builder<T> anImmutableMap(Map<T, String> x);",
1104             "    public abstract ImmutableMap.Builder<T, String> anImmutableMapBuilder();",
1105             "    public abstract Builder<T> anOptionalString(Optional<String> s);",
1106             "    public abstract Builder<T> anOptionalString(String s);",
1107             "    public abstract NestedAutoValue.Builder<T> aNestedAutoValueBuilder();",
1108             "",
1109             "    public Builder<T> aList(ArrayList<T> x) {",
1110             // ArrayList should not be imported in the generated class.
1111             "      return aList((List<T>) x);",
1112             "    }",
1113             "",
1114             "    public abstract Optional<Integer> anInt();",
1115             "    public abstract List<T> aList();",
1116             "    public abstract ImmutableMap<T, String> anImmutableMap();",
1117             "",
1118             "    public abstract Baz<T> build();",
1119             "  }",
1120             "",
1121             "  public static <T extends Number> Builder<T> builder() {",
1122             "    return AutoValue_Baz.builder();",
1123             "  }",
1124             "}");
1125     JavaFileObject nestedJavaFileObject =
1126         JavaFileObjects.forSourceLines(
1127             "foo.bar.NestedAutoValue",
1128             "package foo.bar;",
1129             "",
1130             "import com.google.auto.value.AutoValue;",
1131             "",
1132             "@AutoValue",
1133             "public abstract class NestedAutoValue<T extends Number> {",
1134             "  public abstract T t();",
1135             "",
1136             "  public abstract Builder<T> toBuilder();",
1137             "",
1138             "  @AutoValue.Builder",
1139             "  public abstract static class Builder<T extends Number> {",
1140             "    public abstract Builder<T> t(T t);",
1141             "    public abstract NestedAutoValue<T> build();",
1142             "  }",
1143             "",
1144             "  public static <T extends Number> Builder<T> builder() {",
1145             "    return AutoValue_NestedAutoValue.builder();",
1146             "  }",
1147             "}");
1148     JavaFileObject expectedOutput =
1149         JavaFileObjects.forSourceLines(
1150             "foo.bar.AutoValue_Baz",
1151             "package foo.bar;",
1152             "",
1153             "import com.google.common.base.Optional;",
1154             "import com.google.common.collect.ImmutableMap;",
1155             "import java.util.Arrays;",
1156             "import java.util.List;",
1157             "import java.util.Map;",
1158             sorted(
1159                 GeneratedImport.importGeneratedAnnotationType(),
1160                 "import javax.annotation.Nullable;"),
1161             "",
1162             "@Generated(\"" + AutoValueProcessor.class.getName() + "\")",
1163             "final class AutoValue_Baz<T extends Number> extends Baz<T> {",
1164             "  private final int anInt;",
1165             "  private final byte[] aByteArray;",
1166             "  @Nullable",
1167             "  private final int[] aNullableIntArray;",
1168             "  private final List<T> aList;",
1169             "  private final ImmutableMap<T, String> anImmutableMap;",
1170             "  private final Optional<String> anOptionalString;",
1171             "  private final NestedAutoValue<T> aNestedAutoValue;",
1172             "",
1173             "  private AutoValue_Baz(",
1174             "      int anInt,",
1175             "      byte[] aByteArray,",
1176             "      @Nullable int[] aNullableIntArray,",
1177             "      List<T> aList,",
1178             "      ImmutableMap<T, String> anImmutableMap,",
1179             "      Optional<String> anOptionalString,",
1180             "      NestedAutoValue<T> aNestedAutoValue) {",
1181             "    this.anInt = anInt;",
1182             "    this.aByteArray = aByteArray;",
1183             "    this.aNullableIntArray = aNullableIntArray;",
1184             "    this.aList = aList;",
1185             "    this.anImmutableMap = anImmutableMap;",
1186             "    this.anOptionalString = anOptionalString;",
1187             "    this.aNestedAutoValue = aNestedAutoValue;",
1188             "  }",
1189             "",
1190             "  @Override public int anInt() {",
1191             "    return anInt;",
1192             "  }",
1193             "",
1194             "  @SuppressWarnings(\"mutable\")",
1195             "  @Override public byte[] aByteArray() {",
1196             "    return aByteArray;",
1197             "  }",
1198             "",
1199             "  @SuppressWarnings(\"mutable\")",
1200             "  @Nullable",
1201             "  @Override public int[] aNullableIntArray() {",
1202             "    return aNullableIntArray;",
1203             "  }",
1204             "",
1205             "  @Override public List<T> aList() {",
1206             "    return aList;",
1207             "  }",
1208             "",
1209             "  @Override public ImmutableMap<T, String> anImmutableMap() {",
1210             "    return anImmutableMap;",
1211             "  }",
1212             "",
1213             "  @Override public Optional<String> anOptionalString() {",
1214             "    return anOptionalString;",
1215             "  }",
1216             "",
1217             "  @Override public NestedAutoValue<T> aNestedAutoValue() {",
1218             "    return aNestedAutoValue;",
1219             "  }",
1220             "",
1221             "  @Override public String toString() {",
1222             "    return \"Baz{\"",
1223             "        + \"anInt=\" + anInt + \", \"",
1224             "        + \"aByteArray=\" + Arrays.toString(aByteArray) + \", \"",
1225             "        + \"aNullableIntArray=\" + Arrays.toString(aNullableIntArray) + \", \"",
1226             "        + \"aList=\" + aList + \", \"",
1227             "        + \"anImmutableMap=\" + anImmutableMap + \", \"",
1228             "        + \"anOptionalString=\" + anOptionalString + \", \"",
1229             "        + \"aNestedAutoValue=\" + aNestedAutoValue",
1230             "        + \"}\";",
1231             "  }",
1232             "",
1233             "  @Override public boolean equals(Object o) {",
1234             "    if (o == this) {",
1235             "      return true;",
1236             "    }",
1237             "    if (o instanceof Baz) {",
1238             "      Baz<?> that = (Baz<?>) o;",
1239             "      return this.anInt == that.anInt()",
1240             "          && Arrays.equals(this.aByteArray, "
1241                 + "(that instanceof AutoValue_Baz) "
1242                 + "? ((AutoValue_Baz<?>) that).aByteArray : that.aByteArray())",
1243             "          && Arrays.equals(this.aNullableIntArray, "
1244                 + "(that instanceof AutoValue_Baz) "
1245                 + "? ((AutoValue_Baz<?>) that).aNullableIntArray : that.aNullableIntArray())",
1246             "          && this.aList.equals(that.aList())",
1247             "          && this.anImmutableMap.equals(that.anImmutableMap())",
1248             "          && this.anOptionalString.equals(that.anOptionalString())",
1249             "          && this.aNestedAutoValue.equals(that.aNestedAutoValue());",
1250             "    }",
1251             "    return false;",
1252             "  }",
1253             "",
1254             "  @Override public int hashCode() {",
1255             "    int h$ = 1;",
1256             "    h$ *= 1000003;",
1257             "    h$ ^= anInt;",
1258             "    h$ *= 1000003;",
1259             "    h$ ^= Arrays.hashCode(aByteArray);",
1260             "    h$ *= 1000003;",
1261             "    h$ ^= Arrays.hashCode(aNullableIntArray);",
1262             "    h$ *= 1000003;",
1263             "    h$ ^= aList.hashCode();",
1264             "    h$ *= 1000003;",
1265             "    h$ ^= anImmutableMap.hashCode();",
1266             "    h$ *= 1000003;",
1267             "    h$ ^= anOptionalString.hashCode();",
1268             "    h$ *= 1000003;",
1269             "    h$ ^= aNestedAutoValue.hashCode();",
1270             "    return h$;",
1271             "  }",
1272             "",
1273             "  @Override public Baz.Builder<T> toBuilder() {",
1274             "    return new Builder<T>(this);",
1275             "  }",
1276             "",
1277             "  static final class Builder<T extends Number> extends Baz.Builder<T> {",
1278             "    private int anInt;",
1279             "    private byte[] aByteArray;",
1280             "    private int[] aNullableIntArray;",
1281             "    private List<T> aList;",
1282             "    private ImmutableMap.Builder<T, String> anImmutableMapBuilder$;",
1283             "    private ImmutableMap<T, String> anImmutableMap;",
1284             "    private Optional<String> anOptionalString = Optional.absent();",
1285             "    private NestedAutoValue.Builder<T> aNestedAutoValueBuilder$;",
1286             "    private NestedAutoValue<T> aNestedAutoValue;",
1287             "    private byte set$0;",
1288             "",
1289             "    Builder() {",
1290             "    }",
1291             "",
1292             "    private Builder(Baz<T> source) {",
1293             "      this.anInt = source.anInt();",
1294             "      this.aByteArray = source.aByteArray();",
1295             "      this.aNullableIntArray = source.aNullableIntArray();",
1296             "      this.aList = source.aList();",
1297             "      this.anImmutableMap = source.anImmutableMap();",
1298             "      this.anOptionalString = source.anOptionalString();",
1299             "      this.aNestedAutoValue = source.aNestedAutoValue();",
1300             "      set$0 = (byte) 1;",
1301             "    }",
1302             "",
1303             "    @Override",
1304             "    public Baz.Builder<T> anInt(int anInt) {",
1305             "      this.anInt = anInt;",
1306             "      set$0 |= (byte) 1;",
1307             "      return this;",
1308             "    }",
1309             "",
1310             "    @Override",
1311             "    public Optional<Integer> anInt() {",
1312             "      if ((set$0 & 1) == 0) {",
1313             "        return Optional.absent();",
1314             "      }",
1315             "      return Optional.of(anInt);",
1316             "    }",
1317             "",
1318             "    @Override",
1319             "    public Baz.Builder<T> aByteArray(byte[] aByteArray) {",
1320             "      if (aByteArray == null) {",
1321             "        throw new NullPointerException(\"Null aByteArray\");",
1322             "      }",
1323             "      this.aByteArray = aByteArray;",
1324             "      return this;",
1325             "    }",
1326             "",
1327             "    @Override",
1328             "    public Baz.Builder<T> aNullableIntArray(@Nullable int[] aNullableIntArray) {",
1329             "      this.aNullableIntArray = aNullableIntArray;",
1330             "      return this;",
1331             "    }",
1332             "",
1333             "    @Override",
1334             "    public Baz.Builder<T> aList(List<T> aList) {",
1335             "      if (aList == null) {",
1336             "        throw new NullPointerException(\"Null aList\");",
1337             "      }",
1338             "      this.aList = aList;",
1339             "      return this;",
1340             "    }",
1341             "",
1342             "    @Override",
1343             "    public List<T> aList() {",
1344             "      if (this.aList == null) {",
1345             "        throw new IllegalStateException(\"Property \\\"aList\\\" has not been set\");",
1346             "      }",
1347             "      return aList;",
1348             "    }",
1349             "",
1350             "    @Override",
1351             "    public Baz.Builder<T> anImmutableMap(Map<T, String> anImmutableMap) {",
1352             "      if (anImmutableMapBuilder$ != null) {",
1353             "        throw new IllegalStateException("
1354                 + "\"Cannot set anImmutableMap after calling anImmutableMapBuilder()\");",
1355             "      }",
1356             "      this.anImmutableMap = ImmutableMap.copyOf(anImmutableMap);",
1357             "      return this;",
1358             "    }",
1359             "",
1360             "    @Override",
1361             "    public ImmutableMap.Builder<T, String> anImmutableMapBuilder() {",
1362             "      if (anImmutableMapBuilder$ == null) {",
1363             "        if (anImmutableMap == null) {",
1364             "          anImmutableMapBuilder$ = ImmutableMap.builder();",
1365             "        } else {",
1366             "          anImmutableMapBuilder$ = ImmutableMap.builder();",
1367             "          anImmutableMapBuilder$.putAll(anImmutableMap);",
1368             "          anImmutableMap = null;",
1369             "        }",
1370             "      }",
1371             "      return anImmutableMapBuilder$;",
1372             "    }",
1373             "",
1374             "    @Override",
1375             "    public ImmutableMap<T, String> anImmutableMap() {",
1376             "      if (anImmutableMapBuilder$ != null) {",
1377             "        return anImmutableMapBuilder$.buildOrThrow();",
1378             "      }",
1379             "      if (anImmutableMap == null) {",
1380             "        anImmutableMap = ImmutableMap.of();",
1381             "      }",
1382             "      return anImmutableMap;",
1383             "    }",
1384             "",
1385             "    @Override",
1386             "    public Baz.Builder<T> anOptionalString(Optional<String> anOptionalString) {",
1387             "      if (anOptionalString == null) {",
1388             "        throw new NullPointerException(\"Null anOptionalString\");",
1389             "      }",
1390             "      this.anOptionalString = anOptionalString;",
1391             "      return this;",
1392             "    }",
1393             "",
1394             "    @Override",
1395             "    public Baz.Builder<T> anOptionalString(String anOptionalString) {",
1396             "      this.anOptionalString = Optional.of(anOptionalString);",
1397             "      return this;",
1398             "    }",
1399             "",
1400             "    @Override",
1401             "    public NestedAutoValue.Builder<T> aNestedAutoValueBuilder() {",
1402             "      if (aNestedAutoValueBuilder$ == null) {",
1403             "        if (aNestedAutoValue == null) {",
1404             "          aNestedAutoValueBuilder$ = NestedAutoValue.builder();",
1405             "        } else {",
1406             "          aNestedAutoValueBuilder$ = aNestedAutoValue.toBuilder();",
1407             "          aNestedAutoValue = null;",
1408             "        }",
1409             "      }",
1410             "      return aNestedAutoValueBuilder$;",
1411             "    }",
1412             "",
1413             "    @Override",
1414             "    public Baz<T> build() {",
1415             "      if (anImmutableMapBuilder$ != null) {",
1416             "        this.anImmutableMap = anImmutableMapBuilder$.buildOrThrow();",
1417             "      } else if (this.anImmutableMap == null) {",
1418             "        this.anImmutableMap = ImmutableMap.of();",
1419             "      }",
1420             "      if (aNestedAutoValueBuilder$ != null) {",
1421             "        this.aNestedAutoValue = aNestedAutoValueBuilder$.build();",
1422             "      } else if (this.aNestedAutoValue == null) {",
1423             "        NestedAutoValue.Builder<T> aNestedAutoValue$builder = "
1424                 + "NestedAutoValue.builder();",
1425             "        this.aNestedAutoValue = aNestedAutoValue$builder.build();",
1426             "      }",
1427             "      if (set$0 != 1",
1428             "          || this.aByteArray == null",
1429             "          || this.aList == null) {",
1430             "        StringBuilder missing = new StringBuilder();",
1431             "        if ((set$0 & 1) == 0) {",
1432             "            missing.append(\" anInt\");",
1433             "        }",
1434             "        if (this.aByteArray == null) {",
1435             "          missing.append(\" aByteArray\");",
1436             "        }",
1437             "        if (this.aList == null) {",
1438             "          missing.append(\" aList\");",
1439             "        }",
1440             "        throw new IllegalStateException(\"Missing required properties:\" + missing);",
1441             "      }",
1442             "      return new AutoValue_Baz<T>(",
1443             "          this.anInt,",
1444             "          this.aByteArray,",
1445             "          this.aNullableIntArray,",
1446             "          this.aList,",
1447             "          this.anImmutableMap,",
1448             "          this.anOptionalString,",
1449             "          this.aNestedAutoValue);",
1450             "    }",
1451             "  }",
1452             "}");
1453     Compilation compilation =
1454         javac()
1455             .withProcessors(new AutoValueProcessor())
1456             .withOptions(
1457                 "-Xlint:-processing", "-implicit:none", "-A" + Nullables.NULLABLE_OPTION + "=")
1458             .compile(javaFileObject, nestedJavaFileObject);
1459     assertThat(compilation).succeededWithoutWarnings();
1460     assertThat(compilation)
1461         .generatedSourceFile("foo.bar.AutoValue_Baz")
1462         .hasSourceEquivalentTo(expectedOutput);
1463   }
1464 
1465   @Test
builderWithNullableTypeAnnotation()1466   public void builderWithNullableTypeAnnotation() {
1467     assume().that(typeAnnotationsWork).isTrue();
1468     JavaFileObject javaFileObject =
1469         JavaFileObjects.forSourceLines(
1470             "foo.bar.Baz",
1471             "package foo.bar;",
1472             "",
1473             "import com.google.auto.value.AutoValue;",
1474             "import com.google.common.base.Optional;",
1475             "import com.google.common.collect.ImmutableMap;",
1476             "",
1477             "import java.util.ArrayList;",
1478             "import java.util.List;",
1479             "import java.util.Map;",
1480             "import org.checkerframework.checker.nullness.qual.Nullable;",
1481             "",
1482             "@AutoValue",
1483             "public abstract class Baz<T extends Number> {",
1484             "  public abstract int anInt();",
1485             "  @SuppressWarnings(\"mutable\")",
1486             "  public abstract byte[] aByteArray();",
1487             "  @SuppressWarnings(\"mutable\")",
1488             "  public abstract int @Nullable [] aNullableIntArray();",
1489             "  public abstract List<T> aList();",
1490             "  public abstract ImmutableMap<T, String> anImmutableMap();",
1491             "  public abstract Optional<String> anOptionalString();",
1492             "",
1493             "  public abstract Builder<T> toBuilder();",
1494             "",
1495             "  @AutoValue.Builder",
1496             "  public abstract static class Builder<T extends Number> {",
1497             "    public abstract Builder<T> anInt(int x);",
1498             "    public abstract Builder<T> aByteArray(byte[] x);",
1499             "    public abstract Builder<T> aNullableIntArray(int @Nullable [] x);",
1500             "    public abstract Builder<T> aList(List<T> x);",
1501             "    public abstract Builder<T> anImmutableMap(Map<T, String> x);",
1502             "    public abstract ImmutableMap.Builder<T, String> anImmutableMapBuilder();",
1503             "    public abstract Builder<T> anOptionalString(Optional<String> s);",
1504             "    public abstract Baz<T> build();",
1505             "  }",
1506             "",
1507             "  public static <T extends Number> Builder<T> builder() {",
1508             "    return AutoValue_Baz.builder();",
1509             "  }",
1510             "}");
1511     JavaFileObject expectedOutput =
1512         JavaFileObjects.forSourceLines(
1513             "foo.bar.AutoValue_Baz",
1514             "package foo.bar;",
1515             "",
1516             "import com.google.common.base.Optional;",
1517             "import com.google.common.collect.ImmutableMap;",
1518             "import java.util.Arrays;",
1519             "import java.util.List;",
1520             "import java.util.Map;",
1521             sorted(
1522                 GeneratedImport.importGeneratedAnnotationType(),
1523                 "import org.checkerframework.checker.nullness.qual.Nullable;"),
1524             "",
1525             "@Generated(\"" + AutoValueProcessor.class.getName() + "\")",
1526             "final class AutoValue_Baz<T extends Number> extends Baz<T> {",
1527             "  private final int anInt;",
1528             "  private final byte[] aByteArray;",
1529             "  private final int @Nullable [] aNullableIntArray;",
1530             "  private final List<T> aList;",
1531             "  private final ImmutableMap<T, String> anImmutableMap;",
1532             "  private final Optional<String> anOptionalString;",
1533             "",
1534             "  private AutoValue_Baz(",
1535             "      int anInt,",
1536             "      byte[] aByteArray,",
1537             "      int @Nullable [] aNullableIntArray,",
1538             "      List<T> aList,",
1539             "      ImmutableMap<T, String> anImmutableMap,",
1540             "      Optional<String> anOptionalString) {",
1541             "    this.anInt = anInt;",
1542             "    this.aByteArray = aByteArray;",
1543             "    this.aNullableIntArray = aNullableIntArray;",
1544             "    this.aList = aList;",
1545             "    this.anImmutableMap = anImmutableMap;",
1546             "    this.anOptionalString = anOptionalString;",
1547             "  }",
1548             "",
1549             "  @Override public int anInt() {",
1550             "    return anInt;",
1551             "  }",
1552             "",
1553             "  @SuppressWarnings(\"mutable\")",
1554             "  @Override public byte[] aByteArray() {",
1555             "    return aByteArray;",
1556             "  }",
1557             "",
1558             "  @SuppressWarnings(\"mutable\")",
1559             "  @Override public int @Nullable [] aNullableIntArray() {",
1560             "    return aNullableIntArray;",
1561             "  }",
1562             "",
1563             "  @Override public List<T> aList() {",
1564             "    return aList;",
1565             "  }",
1566             "",
1567             "  @Override public ImmutableMap<T, String> anImmutableMap() {",
1568             "    return anImmutableMap;",
1569             "  }",
1570             "",
1571             "  @Override public Optional<String> anOptionalString() {",
1572             "    return anOptionalString;",
1573             "  }",
1574             "",
1575             "  @Override public String toString() {",
1576             "    return \"Baz{\"",
1577             "        + \"anInt=\" + anInt + \", \"",
1578             "        + \"aByteArray=\" + Arrays.toString(aByteArray) + \", \"",
1579             "        + \"aNullableIntArray=\" + Arrays.toString(aNullableIntArray) + \", \"",
1580             "        + \"aList=\" + aList + \", \"",
1581             "        + \"anImmutableMap=\" + anImmutableMap + \", \"",
1582             "        + \"anOptionalString=\" + anOptionalString",
1583             "        + \"}\";",
1584             "  }",
1585             "",
1586             "  @Override public boolean equals(@Nullable Object o) {",
1587             "    if (o == this) {",
1588             "      return true;",
1589             "    }",
1590             "    if (o instanceof Baz) {",
1591             "      Baz<?> that = (Baz<?>) o;",
1592             "      return this.anInt == that.anInt()",
1593             "          && Arrays.equals(this.aByteArray, "
1594                 + "(that instanceof AutoValue_Baz) "
1595                 + "? ((AutoValue_Baz<?>) that).aByteArray : that.aByteArray())",
1596             "          && Arrays.equals(this.aNullableIntArray, "
1597                 + "(that instanceof AutoValue_Baz) "
1598                 + "? ((AutoValue_Baz<?>) that).aNullableIntArray : that.aNullableIntArray())",
1599             "          && this.aList.equals(that.aList())",
1600             "          && this.anImmutableMap.equals(that.anImmutableMap())",
1601             "          && this.anOptionalString.equals(that.anOptionalString());",
1602             "    }",
1603             "    return false;",
1604             "  }",
1605             "",
1606             "  @Override public int hashCode() {",
1607             "    int h$ = 1;",
1608             "    h$ *= 1000003;",
1609             "    h$ ^= anInt;",
1610             "    h$ *= 1000003;",
1611             "    h$ ^= Arrays.hashCode(aByteArray);",
1612             "    h$ *= 1000003;",
1613             "    h$ ^= Arrays.hashCode(aNullableIntArray);",
1614             "    h$ *= 1000003;",
1615             "    h$ ^= aList.hashCode();",
1616             "    h$ *= 1000003;",
1617             "    h$ ^= anImmutableMap.hashCode();",
1618             "    h$ *= 1000003;",
1619             "    h$ ^= anOptionalString.hashCode();",
1620             "    return h$;",
1621             "  }",
1622             "",
1623             "  @Override public Baz.Builder<T> toBuilder() {",
1624             "    return new Builder<T>(this);",
1625             "  }",
1626             "",
1627             "  static final class Builder<T extends Number> extends Baz.Builder<T> {",
1628             "    private int anInt;",
1629             "    private byte @Nullable [] aByteArray;",
1630             "    private int @Nullable [] aNullableIntArray;",
1631             "    private @Nullable List<T> aList;",
1632             "    private ImmutableMap.@Nullable Builder<T, String> anImmutableMapBuilder$;",
1633             "    private @Nullable ImmutableMap<T, String> anImmutableMap;",
1634             "    private Optional<String> anOptionalString = Optional.absent();",
1635             "    private byte set$0;",
1636             "",
1637             "    Builder() {",
1638             "    }",
1639             "",
1640             "    private Builder(Baz<T> source) {",
1641             "      this.anInt = source.anInt();",
1642             "      this.aByteArray = source.aByteArray();",
1643             "      this.aNullableIntArray = source.aNullableIntArray();",
1644             "      this.aList = source.aList();",
1645             "      this.anImmutableMap = source.anImmutableMap();",
1646             "      this.anOptionalString = source.anOptionalString();",
1647             "      set$0 = (byte) 1;",
1648             "    }",
1649             "",
1650             "    @Override",
1651             "    public Baz.Builder<T> anInt(int anInt) {",
1652             "      this.anInt = anInt;",
1653             "      set$0 |= (byte) 1;",
1654             "      return this;",
1655             "    }",
1656             "",
1657             "    @Override",
1658             "    public Baz.Builder<T> aByteArray(byte[] aByteArray) {",
1659             "      if (aByteArray == null) {",
1660             "        throw new NullPointerException(\"Null aByteArray\");",
1661             "      }",
1662             "      this.aByteArray = aByteArray;",
1663             "      return this;",
1664             "    }",
1665             "",
1666             "    @Override",
1667             "    public Baz.Builder<T> aNullableIntArray(int @Nullable [] aNullableIntArray) {",
1668             "      this.aNullableIntArray = aNullableIntArray;",
1669             "      return this;",
1670             "    }",
1671             "",
1672             "    @Override",
1673             "    public Baz.Builder<T> aList(List<T> aList) {",
1674             "      if (aList == null) {",
1675             "        throw new NullPointerException(\"Null aList\");",
1676             "      }",
1677             "      this.aList = aList;",
1678             "      return this;",
1679             "    }",
1680             "",
1681             "    @Override",
1682             "    public Baz.Builder<T> anImmutableMap(Map<T, String> anImmutableMap) {",
1683             "      if (anImmutableMapBuilder$ != null) {",
1684             "        throw new IllegalStateException("
1685                 + "\"Cannot set anImmutableMap after calling anImmutableMapBuilder()\");",
1686             "      }",
1687             "      this.anImmutableMap = ImmutableMap.copyOf(anImmutableMap);",
1688             "      return this;",
1689             "    }",
1690             "",
1691             "    @Override",
1692             "    public ImmutableMap.Builder<T, String> anImmutableMapBuilder() {",
1693             "      if (anImmutableMapBuilder$ == null) {",
1694             "        if (anImmutableMap == null) {",
1695             "          anImmutableMapBuilder$ = ImmutableMap.builder();",
1696             "        } else {",
1697             "          anImmutableMapBuilder$ = ImmutableMap.builder();",
1698             "          anImmutableMapBuilder$.putAll(anImmutableMap);",
1699             "          anImmutableMap = null;",
1700             "        }",
1701             "      }",
1702             "      return anImmutableMapBuilder$;",
1703             "    }",
1704             "",
1705             "    @Override",
1706             "    public Baz.Builder<T> anOptionalString(Optional<String> anOptionalString) {",
1707             "      if (anOptionalString == null) {",
1708             "        throw new NullPointerException(\"Null anOptionalString\");",
1709             "      }",
1710             "      this.anOptionalString = anOptionalString;",
1711             "      return this;",
1712             "    }",
1713             "",
1714             "    @Override",
1715             "    public Baz<T> build() {",
1716             "      if (anImmutableMapBuilder$ != null) {",
1717             "        this.anImmutableMap = anImmutableMapBuilder$.buildOrThrow();",
1718             "      } else if (this.anImmutableMap == null) {",
1719             "        this.anImmutableMap = ImmutableMap.of();",
1720             "      }",
1721             "      if (set$0 != 1",
1722             "          || this.aByteArray == null",
1723             "          || this.aList == null) {",
1724             "        StringBuilder missing = new StringBuilder();",
1725             "        if ((set$0 & 1) == 0) {",
1726             "            missing.append(\" anInt\");",
1727             "        }",
1728             "        if (this.aByteArray == null) {",
1729             "          missing.append(\" aByteArray\");",
1730             "        }",
1731             "        if (this.aList == null) {",
1732             "          missing.append(\" aList\");",
1733             "        }",
1734             "        throw new IllegalStateException(\"Missing required properties:\" + missing);",
1735             "      }",
1736             "      return new AutoValue_Baz<T>(",
1737             "          this.anInt,",
1738             "          this.aByteArray,",
1739             "          this.aNullableIntArray,",
1740             "          this.aList,",
1741             "          this.anImmutableMap,",
1742             "          this.anOptionalString);",
1743             "    }",
1744             "  }",
1745             "}");
1746     Compilation compilation =
1747         javac()
1748             .withProcessors(new AutoValueProcessor())
1749             .withOptions(
1750                 "-Xlint:-processing",
1751                 "-implicit:none",
1752                 "-A" + Nullables.NULLABLE_OPTION + "=org.checkerframework.checker.nullness.qual.Nullable")
1753             .compile(javaFileObject);
1754     assertThat(compilation).succeededWithoutWarnings();
1755     assertThat(compilation)
1756         .generatedSourceFile("foo.bar.AutoValue_Baz")
1757         .hasSourceEquivalentTo(expectedOutput);
1758   }
1759 
1760   @Test
autoValueBuilderOnTopLevelClass()1761   public void autoValueBuilderOnTopLevelClass() {
1762     JavaFileObject javaFileObject =
1763         JavaFileObjects.forSourceLines(
1764             "foo.bar.Builder",
1765             "package foo.bar;",
1766             "",
1767             "import com.google.auto.value.AutoValue;",
1768             "",
1769             "@AutoValue.Builder",
1770             "public interface Builder {",
1771             "  Builder foo(int x);",
1772             "  Object build();",
1773             "}");
1774     Compilation compilation =
1775         javac()
1776             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
1777             .compile(javaFileObject);
1778     assertThat(compilation)
1779         .hadErrorContaining("can only be applied to a class or interface inside")
1780         .inFile(javaFileObject)
1781         .onLineContaining("public interface Builder");
1782   }
1783 
1784   @Test
autoValueBuilderNotInsideAutoValue()1785   public void autoValueBuilderNotInsideAutoValue() {
1786     JavaFileObject javaFileObject =
1787         JavaFileObjects.forSourceLines(
1788             "foo.bar.Baz",
1789             "package foo.bar;",
1790             "",
1791             "import com.google.auto.value.AutoValue;",
1792             "",
1793             "public abstract class Baz {",
1794             "  abstract int foo();",
1795             "",
1796             "  static Builder builder() {",
1797             "    return new AutoValue_Baz.Builder();",
1798             "  }",
1799             "",
1800             "  @AutoValue.Builder",
1801             "  public interface Builder {",
1802             "    Builder foo(int x);",
1803             "    Baz build();",
1804             "  }",
1805             "}");
1806     Compilation compilation =
1807         javac()
1808             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
1809             .compile(javaFileObject);
1810     assertThat(compilation)
1811         .hadErrorContaining("can only be applied to a class or interface inside")
1812         .inFile(javaFileObject)
1813         .onLineContaining("public interface Builder");
1814   }
1815 
1816   @Test
autoValueBuilderNotStatic()1817   public void autoValueBuilderNotStatic() {
1818     JavaFileObject javaFileObject =
1819         JavaFileObjects.forSourceLines(
1820             "foo.bar.Example",
1821             "package foo.bar;",
1822             "",
1823             "import com.google.auto.value.AutoValue;",
1824             "",
1825             "class Example {",
1826             "  @AutoValue",
1827             "  abstract static class Baz {",
1828             "    abstract int foo();",
1829             "",
1830             "    static Builder builder() {",
1831             "      return new AutoValue_Example_Baz.Builder();",
1832             "    }",
1833             "",
1834             "    @AutoValue.Builder",
1835             "    abstract class Builder {",
1836             "      abstract Builder foo(int x);",
1837             "      abstract Baz build();",
1838             "    }",
1839             "  }",
1840             "}");
1841     Compilation compilation =
1842         javac()
1843             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
1844             .compile(javaFileObject);
1845     assertThat(compilation)
1846         .hadErrorContaining("@AutoValue.Builder cannot be applied to a non-static class")
1847         .inFile(javaFileObject)
1848         .onLineContaining("abstract class Builder");
1849   }
1850 
1851   @Test
autoValueBuilderMustHaveNoArgConstructor()1852   public void autoValueBuilderMustHaveNoArgConstructor() {
1853     JavaFileObject javaFileObject =
1854         JavaFileObjects.forSourceLines(
1855             "foo.bar.Example",
1856             "package foo.bar;",
1857             "",
1858             "import com.google.auto.value.AutoValue;",
1859             "",
1860             "class Example {",
1861             "  @AutoValue",
1862             "  abstract static class Baz {",
1863             "    abstract int foo();",
1864             "",
1865             "    static Builder builder() {",
1866             "      return new AutoValue_Example_Baz.Builder();",
1867             "    }",
1868             "",
1869             "    @AutoValue.Builder",
1870             "    abstract static class Builder {",
1871             "      Builder(int defaultFoo) {}",
1872             "      abstract Builder foo(int x);",
1873             "      abstract Baz build();",
1874             "    }",
1875             "  }",
1876             "}");
1877     Compilation compilation =
1878         javac()
1879             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
1880             .compile(javaFileObject);
1881     assertThat(compilation)
1882         .hadErrorContaining("@AutoValue.Builder class must have a non-private no-arg constructor")
1883         .inFile(javaFileObject)
1884         .onLineContaining("class Builder");
1885   }
1886 
1887   @Test
autoValueBuilderOnEnum()1888   public void autoValueBuilderOnEnum() {
1889     JavaFileObject javaFileObject =
1890         JavaFileObjects.forSourceLines(
1891             "foo.bar.Baz",
1892             "package foo.bar;",
1893             "",
1894             "import com.google.auto.value.AutoValue;",
1895             "",
1896             "@AutoValue",
1897             "public abstract class Baz {",
1898             "  abstract int foo();",
1899             "",
1900             "  static Builder builder() {",
1901             "    return null;",
1902             "  }",
1903             "",
1904             "  @AutoValue.Builder",
1905             "  public enum Builder {}",
1906             "}");
1907     Compilation compilation =
1908         javac()
1909             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
1910             .compile(javaFileObject);
1911     assertThat(compilation)
1912         .hadErrorContaining("can only apply to a class or an interface")
1913         .inFile(javaFileObject)
1914         .onLineContaining("public enum Builder");
1915   }
1916 
1917   @Test
autoValueBuilderDuplicate()1918   public void autoValueBuilderDuplicate() {
1919     JavaFileObject javaFileObject =
1920         JavaFileObjects.forSourceLines(
1921             "foo.bar.Baz",
1922             "package foo.bar;",
1923             "",
1924             "import com.google.auto.value.AutoValue;",
1925             "",
1926             "@AutoValue",
1927             "public abstract class Baz {",
1928             "  @AutoValue.Builder",
1929             "  public interface Builder1 {",
1930             "    Baz build();",
1931             "  }",
1932             "",
1933             "  @AutoValue.Builder",
1934             "  public interface Builder2 {",
1935             "    Baz build();",
1936             "  }",
1937             "}");
1938     Compilation compilation =
1939         javac()
1940             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
1941             .compile(javaFileObject);
1942     assertThat(compilation)
1943         .hadErrorContaining("already has a Builder: foo.bar.Baz.Builder1")
1944         .inFile(javaFileObject)
1945         .onLineContaining("public interface Builder2");
1946   }
1947 
1948   @Test
autoValueBuilderMissingSetter()1949   public void autoValueBuilderMissingSetter() {
1950     JavaFileObject javaFileObject =
1951         JavaFileObjects.forSourceLines(
1952             "foo.bar.Baz",
1953             "package foo.bar;",
1954             "",
1955             "import com.google.auto.value.AutoValue;",
1956             "",
1957             "@AutoValue",
1958             "public abstract class Baz {",
1959             "  abstract int blim();",
1960             "  abstract String blam();",
1961             "",
1962             "  @AutoValue.Builder",
1963             "  public interface Builder {",
1964             "    Builder blam(String x);",
1965             "    Baz build();",
1966             "  }",
1967             "}");
1968     Compilation compilation =
1969         javac()
1970             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
1971             .compile(javaFileObject);
1972     assertThat(compilation)
1973         .hadErrorContaining("with this signature: foo.bar.Baz.Builder blim(int)")
1974         .inFile(javaFileObject)
1975         .onLineContaining("public interface Builder");
1976   }
1977 
1978   @Test
autoValueBuilderMissingSetterUsingSetPrefix()1979   public void autoValueBuilderMissingSetterUsingSetPrefix() {
1980     JavaFileObject javaFileObject =
1981         JavaFileObjects.forSourceLines(
1982             "foo.bar.Baz",
1983             "package foo.bar;",
1984             "",
1985             "import com.google.auto.value.AutoValue;",
1986             "",
1987             "@AutoValue",
1988             "public abstract class Baz {",
1989             "  abstract int blim();",
1990             "  abstract String blam();",
1991             "",
1992             "  @AutoValue.Builder",
1993             "  public interface Builder {",
1994             "    Builder setBlam(String x);",
1995             "    Baz build();",
1996             "  }",
1997             "}");
1998     Compilation compilation =
1999         javac()
2000             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2001             .compile(javaFileObject);
2002     assertThat(compilation)
2003         .hadErrorContaining("with this signature: foo.bar.Baz.Builder setBlim(int)")
2004         .inFile(javaFileObject)
2005         .onLineContaining("public interface Builder");
2006   }
2007 
2008   @Test
autoValueBuilderWrongTypeSetter()2009   public void autoValueBuilderWrongTypeSetter() {
2010     JavaFileObject javaFileObject =
2011         JavaFileObjects.forSourceLines(
2012             "foo.bar.Baz",
2013             "package foo.bar;",
2014             "",
2015             "import com.google.auto.value.AutoValue;",
2016             "",
2017             "@AutoValue",
2018             "public abstract class Baz {",
2019             "  abstract int blim();",
2020             "  abstract String blam();",
2021             "",
2022             "  @AutoValue.Builder",
2023             "  public interface Builder {",
2024             "    Builder blim(String x);",
2025             "    Builder blam(String x);",
2026             "    Baz build();",
2027             "  }",
2028             "}");
2029     Compilation compilation =
2030         javac()
2031             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2032             .compile(javaFileObject);
2033     assertThat(compilation)
2034         .hadErrorContaining(
2035             "Parameter type java.lang.String of setter method should be int "
2036                 + "to match property method foo.bar.Baz.blim()")
2037         .inFile(javaFileObject)
2038         .onLineContaining("Builder blim(String x)");
2039   }
2040 
2041   @Test
autoValueBuilderSetterReturnsNullable()2042   public void autoValueBuilderSetterReturnsNullable() {
2043     JavaFileObject javaFileObject =
2044         JavaFileObjects.forSourceLines(
2045             "foo.bar.Baz",
2046             "package foo.bar;",
2047             "",
2048             "import com.google.auto.value.AutoValue;",
2049             "import javax.annotation.Nullable;",
2050             "",
2051             "@AutoValue",
2052             "public abstract class Baz {",
2053             "  abstract String blam();",
2054             "",
2055             "  @AutoValue.Builder",
2056             "  public interface Builder {",
2057             "    @Nullable Builder blam(String x);",
2058             "    Baz build();",
2059             "  }",
2060             "}");
2061     Compilation compilation =
2062         javac()
2063             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2064             .compile(javaFileObject);
2065     assertThat(compilation)
2066         .hadWarningContaining(
2067             "Setter methods always return the Builder so @Nullable is not appropriate")
2068         .inFile(javaFileObject)
2069         .onLineContaining("Builder blam(String x)");
2070   }
2071 
2072   @Test
autoValueBuilderWrongTypeSetterWithCopyOf()2073   public void autoValueBuilderWrongTypeSetterWithCopyOf() {
2074     JavaFileObject javaFileObject =
2075         JavaFileObjects.forSourceLines(
2076             "foo.bar.Baz",
2077             "package foo.bar;",
2078             "",
2079             "import com.google.auto.value.AutoValue;",
2080             "import com.google.common.collect.ImmutableList;",
2081             "",
2082             "@AutoValue",
2083             "public abstract class Baz {",
2084             "  abstract String blim();",
2085             "  abstract ImmutableList<String> blam();",
2086             "",
2087             "  @AutoValue.Builder",
2088             "  public interface Builder {",
2089             "    Builder blim(String x);",
2090             "    Builder blam(String x);",
2091             "    Baz build();",
2092             "  }",
2093             "}");
2094     Compilation compilation =
2095         javac()
2096             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2097             .compile(javaFileObject);
2098     assertThat(compilation)
2099         .hadErrorContaining(
2100             "Parameter type java.lang.String of setter method should be"
2101                 + " com.google.common.collect.ImmutableList<java.lang.String> to match property"
2102                 + " method foo.bar.Baz.blam(), or it should be a type that can be passed to"
2103                 + " ImmutableList.copyOf")
2104         .inFile(javaFileObject)
2105         .onLineContaining("Builder blam(String x)");
2106   }
2107 
2108   @Test
autoValueBuilderWrongTypeSetterWithCopyOfGenericallyWrong()2109   public void autoValueBuilderWrongTypeSetterWithCopyOfGenericallyWrong() {
2110     JavaFileObject javaFileObject =
2111         JavaFileObjects.forSourceLines(
2112             "foo.bar.Baz",
2113             "package foo.bar;",
2114             "",
2115             "import com.google.auto.value.AutoValue;",
2116             "import com.google.common.collect.ImmutableList;",
2117             "import java.util.Collection;",
2118             "",
2119             "@AutoValue",
2120             "public abstract class Baz {",
2121             "  abstract String blim();",
2122             "  abstract ImmutableList<String> blam();",
2123             "",
2124             "  @AutoValue.Builder",
2125             "  public interface Builder {",
2126             "    Builder blim(String x);",
2127             "    Builder blam(Collection<Integer> x);",
2128             "    Baz build();",
2129             "  }",
2130             "}");
2131     Compilation compilation =
2132         javac()
2133             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2134             .compile(javaFileObject);
2135     assertThat(compilation)
2136         .hadErrorContaining(
2137             "Parameter type java.util.Collection<java.lang.Integer> of setter method should be"
2138                 + " com.google.common.collect.ImmutableList<java.lang.String> to match property"
2139                 + " method foo.bar.Baz.blam(), or it should be a type that can be passed to"
2140                 + " ImmutableList.copyOf to produce"
2141                 + " com.google.common.collect.ImmutableList<java.lang.String>")
2142         .inFile(javaFileObject)
2143         .onLineContaining("Builder blam(Collection<Integer> x)");
2144   }
2145 
2146   @Test
autoValueBuilderWrongTypeSetterWithGetPrefix()2147   public void autoValueBuilderWrongTypeSetterWithGetPrefix() {
2148     JavaFileObject javaFileObject =
2149         JavaFileObjects.forSourceLines(
2150             "foo.bar.Baz",
2151             "package foo.bar;",
2152             "",
2153             "import com.google.auto.value.AutoValue;",
2154             "",
2155             "@AutoValue",
2156             "public abstract class Baz {",
2157             "  abstract int getBlim();",
2158             "  abstract String getBlam();",
2159             "",
2160             "  @AutoValue.Builder",
2161             "  public interface Builder {",
2162             "    Builder blim(String x);",
2163             "    Builder blam(String x);",
2164             "    Baz build();",
2165             "  }",
2166             "}");
2167     Compilation compilation =
2168         javac()
2169             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2170             .compile(javaFileObject);
2171     assertThat(compilation)
2172         .hadErrorContaining(
2173             "Parameter type java.lang.String of setter method should be int "
2174                 + "to match property method foo.bar.Baz.getBlim()")
2175         .inFile(javaFileObject)
2176         .onLineContaining("Builder blim(String x)");
2177   }
2178 
2179   @Test
autoValueBuilderNullableSetterForNonNullable()2180   public void autoValueBuilderNullableSetterForNonNullable() {
2181     JavaFileObject nullableFileObject =
2182         JavaFileObjects.forSourceLines(
2183             "foo.bar.Nullable",
2184             "package foo.bar;",
2185             "",
2186             "import java.lang.annotation.ElementType;",
2187             "import java.lang.annotation.Target;",
2188             "",
2189             "@Target(ElementType.TYPE_USE)",
2190             "public @interface Nullable {}");
2191     JavaFileObject javaFileObject =
2192         JavaFileObjects.forSourceLines(
2193             "foo.bar.Baz",
2194             "package foo.bar;",
2195             "",
2196             "import com.google.auto.value.AutoValue;",
2197             "",
2198             "@AutoValue",
2199             "public abstract class Baz {",
2200             "  abstract String notNull();",
2201             "",
2202             "  @AutoValue.Builder",
2203             "  public interface Builder {",
2204             "    Builder setNotNull(@Nullable String x);",
2205             "    Baz build();",
2206             "  }",
2207             "}");
2208     Compilation compilation =
2209         javac()
2210             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2211             .compile(javaFileObject, nullableFileObject);
2212     assertThat(compilation)
2213         .hadErrorContaining(
2214             "Parameter of setter method is @Nullable but property method"
2215                 + " foo.bar.Baz.notNull() is not")
2216         .inFile(javaFileObject)
2217         .onLineContaining("setNotNull");
2218   }
2219 
2220   // Check that we get a helpful error message if some of your properties look like getters but
2221   // others don't.
2222   @Test
autoValueBuilderBeansConfusion()2223   public void autoValueBuilderBeansConfusion() {
2224     JavaFileObject javaFileObject =
2225         JavaFileObjects.forSourceLines(
2226             "foo.bar.Item",
2227             "package foo.bar;",
2228             "",
2229             "import com.google.auto.value.AutoValue;",
2230             "",
2231             "@AutoValue",
2232             "public abstract class Item {",
2233             "  abstract String getTitle();",
2234             "  abstract boolean hasThumbnail();",
2235             "",
2236             "  @AutoValue.Builder",
2237             "  public interface Builder {",
2238             "    Builder setTitle(String title);",
2239             "    Builder setHasThumbnail(boolean t);",
2240             "    Item build();",
2241             "  }",
2242             "}");
2243     Compilation compilation =
2244         javac()
2245             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2246             .compile(javaFileObject);
2247     assertThat(compilation)
2248         .hadErrorContaining(
2249             "Method setTitle does not correspond to a property method of foo.bar.Item")
2250         .inFile(javaFileObject)
2251         .onLineContaining("Builder setTitle(String title)");
2252     assertThat(compilation)
2253         .hadNoteContaining("hasThumbnail")
2254         .inFile(javaFileObject)
2255         .onLineContaining("Builder setTitle(String title)");
2256   }
2257 
2258   @Test
autoValueBuilderExtraSetter()2259   public void autoValueBuilderExtraSetter() {
2260     JavaFileObject javaFileObject =
2261         JavaFileObjects.forSourceLines(
2262             "foo.bar.Baz",
2263             "package foo.bar;",
2264             "",
2265             "import com.google.auto.value.AutoValue;",
2266             "",
2267             "@AutoValue",
2268             "public abstract class Baz {",
2269             "  abstract String blam();",
2270             "",
2271             "  @AutoValue.Builder",
2272             "  public interface Builder {",
2273             "    Builder blim(int x);",
2274             "    Builder blam(String x);",
2275             "    Baz build();",
2276             "  }",
2277             "}");
2278     Compilation compilation =
2279         javac()
2280             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2281             .compile(javaFileObject);
2282     assertThat(compilation)
2283         .hadErrorContaining("Method blim does not correspond to a property method of foo.bar.Baz")
2284         .inFile(javaFileObject)
2285         .onLineContaining("Builder blim(int x)");
2286   }
2287 
2288   @Test
autoValueBuilderSetPrefixAndNoSetPrefix()2289   public void autoValueBuilderSetPrefixAndNoSetPrefix() {
2290     JavaFileObject javaFileObject =
2291         JavaFileObjects.forSourceLines(
2292             "foo.bar.Baz",
2293             "package foo.bar;",
2294             "",
2295             "import com.google.auto.value.AutoValue;",
2296             "",
2297             "@AutoValue",
2298             "public abstract class Baz {",
2299             "  abstract int blim();",
2300             "  abstract String blam();",
2301             "",
2302             "  @AutoValue.Builder",
2303             "  public interface Builder {",
2304             "    Builder blim(int x);",
2305             "    Builder setBlam(String x);",
2306             "    Baz build();",
2307             "  }",
2308             "}");
2309     Compilation compilation =
2310         javac()
2311             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2312             .compile(javaFileObject);
2313     assertThat(compilation)
2314         .hadErrorContaining("If any setter methods use the setFoo convention then all must")
2315         .inFile(javaFileObject)
2316         .onLineContaining("Builder blim(int x)");
2317   }
2318 
2319   @Test
autoValueBuilderSetterReturnType()2320   public void autoValueBuilderSetterReturnType() {
2321     // We do allow the return type of a setter to be a supertype of the builder type, to support
2322     // step builders. But we don't allow it to be Object.
2323     JavaFileObject javaFileObject =
2324         JavaFileObjects.forSourceLines(
2325             "foo.bar.Baz",
2326             "package foo.bar;",
2327             "",
2328             "import com.google.auto.value.AutoValue;",
2329             "",
2330             "@AutoValue",
2331             "public abstract class Baz {",
2332             "  abstract int blim();",
2333             "",
2334             "  @AutoValue.Builder",
2335             "  public interface Builder {",
2336             "    Object blim(int x);",
2337             "    Baz build();",
2338             "  }",
2339             "}");
2340     Compilation compilation =
2341         javac()
2342             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2343             .compile(javaFileObject);
2344     assertThat(compilation)
2345         .hadErrorContaining("Setter methods must return foo.bar.Baz.Builder")
2346         .inFile(javaFileObject)
2347         .onLineContaining("Object blim(int x)");
2348   }
2349 
2350   @Test
autoValueBuilderWrongTypeGetter()2351   public void autoValueBuilderWrongTypeGetter() {
2352     JavaFileObject javaFileObject =
2353         JavaFileObjects.forSourceLines(
2354             "foo.bar.Baz",
2355             "package foo.bar;",
2356             "",
2357             "import com.google.auto.value.AutoValue;",
2358             "",
2359             "@AutoValue",
2360             "public abstract class Baz<T, U> {",
2361             "  abstract T blim();",
2362             "  abstract U blam();",
2363             "",
2364             "  @AutoValue.Builder",
2365             "  public interface Builder<T, U> {",
2366             "    Builder<T, U> blim(T x);",
2367             "    Builder<T, U> blam(U x);",
2368             "    T blim();",
2369             "    T blam();",
2370             "    Baz<T, U> build();",
2371             "  }",
2372             "}");
2373     Compilation compilation =
2374         javac()
2375             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2376             .compile(javaFileObject);
2377     assertThat(compilation)
2378         .hadErrorContainingMatch(
2379             "Method matches a property of foo\\.bar\\.Baz<T, ?U> but has return type T instead of"
2380                 + " U")
2381         .inFile(javaFileObject)
2382         .onLineContaining("T blam()");
2383     // The <T, ?U> is because we're depending on TypeMirror.toString(), and the JDK actually spells
2384     // this as <T,U> with no space. While it's not completely sound to expect a given string from
2385     // TypeMirror.toString(), in practice it's hard to imagine that it would be anything other
2386     // than "foo.bar.Baz<T,U>" or "foo.bar.Baz<T, U>" given the specification.
2387   }
2388 
2389   @Test
autoValueBuilderPropertyBuilderInvalidType()2390   public void autoValueBuilderPropertyBuilderInvalidType() {
2391     JavaFileObject javaFileObject =
2392         JavaFileObjects.forSourceLines(
2393             "foo.bar.Baz",
2394             "package foo.bar;",
2395             "",
2396             "import com.google.auto.value.AutoValue;",
2397             "",
2398             "@AutoValue",
2399             "public abstract class Baz<T, U> {",
2400             "  abstract String blim();",
2401             "",
2402             "  @AutoValue.Builder",
2403             "  public interface Builder<T, U> {",
2404             "    StringBuilder blimBuilder();",
2405             "    Baz<T, U> build();",
2406             "  }",
2407             "}");
2408     Compilation compilation =
2409         javac()
2410             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2411             .compile(javaFileObject);
2412     assertThat(compilation)
2413         .hadErrorContaining(
2414             "Method looks like a property builder, but it returns java.lang.StringBuilder which "
2415                 + "does not have a non-static build() or buildOrThrow() method")
2416         .inFile(javaFileObject)
2417         .onLineContaining("StringBuilder blimBuilder()");
2418   }
2419 
2420   @Test
autoValueBuilderPropertyBuilderNullable()2421   public void autoValueBuilderPropertyBuilderNullable() {
2422     JavaFileObject javaFileObject =
2423         JavaFileObjects.forSourceLines(
2424             "foo.bar.Baz",
2425             "package foo.bar;",
2426             "",
2427             "import com.google.auto.value.AutoValue;",
2428             "import com.google.common.collect.ImmutableList;",
2429             "",
2430             "@AutoValue",
2431             "public abstract class Baz<T, U> {",
2432             "  @interface Nullable {}",
2433             "  abstract @Nullable ImmutableList<String> strings();",
2434             "",
2435             "  @AutoValue.Builder",
2436             "  public interface Builder<T, U> {",
2437             "    ImmutableList.Builder<String> stringsBuilder();",
2438             "    Baz<T, U> build();",
2439             "  }",
2440             "}");
2441     Compilation compilation =
2442         javac()
2443             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2444             .compile(javaFileObject);
2445     assertThat(compilation)
2446         .hadErrorContaining("Property strings is @Nullable so it cannot have a property builder")
2447         .inFile(javaFileObject)
2448         .onLineContaining("stringsBuilder()");
2449   }
2450 
2451   @Test
autoValueBuilderPropertyBuilderNullableType()2452   public void autoValueBuilderPropertyBuilderNullableType() {
2453     JavaFileObject javaFileObject =
2454         JavaFileObjects.forSourceLines(
2455             "foo.bar.Baz",
2456             "package foo.bar;",
2457             "",
2458             "import com.google.auto.value.AutoValue;",
2459             "import com.google.common.collect.ImmutableList;",
2460             "import java.lang.annotation.ElementType;",
2461             "import java.lang.annotation.Target;",
2462             "",
2463             "@AutoValue",
2464             "public abstract class Baz<T, U> {",
2465             "  @Target(ElementType.TYPE_USE)",
2466             "  @interface Nullable {}",
2467             "  abstract @Nullable ImmutableList<String> strings();",
2468             "",
2469             "  @AutoValue.Builder",
2470             "  public interface Builder<T, U> {",
2471             "    ImmutableList.Builder<String> stringsBuilder();",
2472             "    Baz<T, U> build();",
2473             "  }",
2474             "}");
2475     Compilation compilation =
2476         javac()
2477             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2478             .compile(javaFileObject);
2479     assertThat(compilation)
2480         .hadErrorContaining("Property strings is @Nullable so it cannot have a property builder")
2481         .inFile(javaFileObject)
2482         .onLineContaining("stringsBuilder()");
2483   }
2484 
2485   @Test
autoValueBuilderPropertyBuilderWrongCollectionType()2486   public void autoValueBuilderPropertyBuilderWrongCollectionType() {
2487     JavaFileObject javaFileObject =
2488         JavaFileObjects.forSourceLines(
2489             "foo.bar.Baz",
2490             "package foo.bar;",
2491             "",
2492             "import com.google.auto.value.AutoValue;",
2493             "import com.google.common.collect.ImmutableList;",
2494             "import com.google.common.collect.ImmutableSet;",
2495             "",
2496             "@AutoValue",
2497             "public abstract class Baz<T, U> {",
2498             "  abstract ImmutableList<T> blim();",
2499             "",
2500             "  @AutoValue.Builder",
2501             "  public interface Builder<T, U> {",
2502             "    ImmutableSet.Builder<T> blimBuilder();",
2503             "    Baz<T, U> build();",
2504             "  }",
2505             "}");
2506     Compilation compilation =
2507         javac()
2508             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2509             .compile(javaFileObject);
2510     assertThat(compilation)
2511         .hadErrorContaining(
2512             "Property builder for blim has type com.google.common.collect.ImmutableSet.Builder "
2513                 + "whose build() method returns com.google.common.collect.ImmutableSet<T> "
2514                 + "instead of com.google.common.collect.ImmutableList<T>")
2515         .inFile(javaFileObject)
2516         .onLineContaining("ImmutableSet.Builder<T> blimBuilder()");
2517   }
2518 
2519   @Test
autoValueBuilderPropertyBuilderWeirdBuilderType()2520   public void autoValueBuilderPropertyBuilderWeirdBuilderType() {
2521     JavaFileObject javaFileObject =
2522         JavaFileObjects.forSourceLines(
2523             "foo.bar.Baz",
2524             "package foo.bar;",
2525             "",
2526             "import com.google.auto.value.AutoValue;",
2527             "import com.google.common.collect.ImmutableSet;",
2528             "",
2529             "@AutoValue",
2530             "public abstract class Baz<T, U> {",
2531             "  abstract Integer blim();",
2532             "",
2533             "  @AutoValue.Builder",
2534             "  public interface Builder<T, U> {",
2535             "    int blimBuilder();",
2536             "    Baz<T, U> build();",
2537             "  }",
2538             "}");
2539     Compilation compilation =
2540         javac()
2541             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2542             .compile(javaFileObject);
2543     assertThat(compilation)
2544         .hadErrorContaining(
2545             "Method looks like a property builder, but its return type is not a class or interface")
2546         .inFile(javaFileObject)
2547         .onLineContaining("int blimBuilder()");
2548   }
2549 
2550   @Test
autoValueBuilderPropertyBuilderWeirdBuiltType()2551   public void autoValueBuilderPropertyBuilderWeirdBuiltType() {
2552     JavaFileObject javaFileObject =
2553         JavaFileObjects.forSourceLines(
2554             "foo.bar.Baz",
2555             "package foo.bar;",
2556             "",
2557             "import com.google.auto.value.AutoValue;",
2558             "import com.google.common.collect.ImmutableSet;",
2559             "",
2560             "@AutoValue",
2561             "public abstract class Baz<T, U> {",
2562             "  abstract int blim();",
2563             "",
2564             "  @AutoValue.Builder",
2565             "  public interface Builder<T, U> {",
2566             "    Integer blimBuilder();",
2567             "    Baz<T, U> build();",
2568             "  }",
2569             "}");
2570     Compilation compilation =
2571         javac()
2572             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2573             .compile(javaFileObject);
2574     assertThat(compilation)
2575         .hadErrorContaining(
2576             "Method looks like a property builder, but the type of property blim is not a class "
2577                 + "or interface")
2578         .inFile(javaFileObject)
2579         .onLineContaining("Integer blimBuilder()");
2580   }
2581 
2582   @Test
autoValueBuilderPropertyBuilderHasNoBuild()2583   public void autoValueBuilderPropertyBuilderHasNoBuild() {
2584     JavaFileObject javaFileObject =
2585         JavaFileObjects.forSourceLines(
2586             "foo.bar.Baz",
2587             "package foo.bar;",
2588             "",
2589             "import com.google.auto.value.AutoValue;",
2590             "import com.google.common.collect.ImmutableSet;",
2591             "",
2592             "@AutoValue",
2593             "public abstract class Baz<T, U> {",
2594             "  abstract String blim();",
2595             "",
2596             "  @AutoValue.Builder",
2597             "  public interface Builder<T, U> {",
2598             "    StringBuilder blimBuilder();",
2599             "    Baz<T, U> build();",
2600             "  }",
2601             "}");
2602     Compilation compilation =
2603         javac()
2604             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2605             .compile(javaFileObject);
2606     assertThat(compilation)
2607         .hadErrorContaining(
2608             "Method looks like a property builder, but it returns java.lang.StringBuilder which "
2609                 + "does not have a non-static build() or buildOrThrow() method")
2610         .inFile(javaFileObject)
2611         .onLineContaining("StringBuilder blimBuilder()");
2612   }
2613 
2614   @Test
autoValueBuilderPropertyBuilderHasStaticBuild()2615   public void autoValueBuilderPropertyBuilderHasStaticBuild() {
2616     JavaFileObject javaFileObject =
2617         JavaFileObjects.forSourceLines(
2618             "foo.bar.Baz",
2619             "package foo.bar;",
2620             "",
2621             "import com.google.auto.value.AutoValue;",
2622             "import com.google.common.collect.ImmutableSet;",
2623             "",
2624             "@AutoValue",
2625             "public abstract class Baz<T, U> {",
2626             "  abstract String blim();",
2627             "",
2628             "  public static class StringFactory {",
2629             "    public static String build() {",
2630             "      return null;",
2631             "    }",
2632             "  }",
2633             "",
2634             "  @AutoValue.Builder",
2635             "  public interface Builder<T, U> {",
2636             "    StringFactory blimBuilder();",
2637             "    Baz<T, U> build();",
2638             "  }",
2639             "}");
2640     Compilation compilation =
2641         javac()
2642             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2643             .compile(javaFileObject);
2644     assertThat(compilation)
2645         .hadErrorContaining(
2646             "Method looks like a property builder, but it returns foo.bar.Baz.StringFactory which "
2647                 + "does not have a non-static build() or buildOrThrow() method")
2648         .inFile(javaFileObject)
2649         .onLineContaining("StringFactory blimBuilder()");
2650   }
2651 
2652   @Test
autoValueBuilderPropertyBuilderReturnsWrongType()2653   public void autoValueBuilderPropertyBuilderReturnsWrongType() {
2654     JavaFileObject javaFileObject =
2655         JavaFileObjects.forSourceLines(
2656             "foo.bar.Baz",
2657             "package foo.bar;",
2658             "",
2659             "import com.google.auto.value.AutoValue;",
2660             "import com.google.common.collect.ImmutableSet;",
2661             "import java.util.List;",
2662             "",
2663             "@AutoValue",
2664             "public abstract class Baz<E> {",
2665             "  abstract List<E> blim();",
2666             "",
2667             "  public static class ListFactory<E> {",
2668             "    public List<? extends E> build() {",
2669             "      return null;",
2670             "    }",
2671             "  }",
2672             "",
2673             "  @AutoValue.Builder",
2674             "  public interface Builder<E> {",
2675             "    ListFactory<E> blimBuilder();",
2676             "    Baz<E> build();",
2677             "  }",
2678             "}");
2679     Compilation compilation =
2680         javac()
2681             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2682             .compile(javaFileObject);
2683     assertThat(compilation)
2684         .hadErrorContaining(
2685             "Property builder for blim has type foo.bar.Baz.ListFactory whose build() method "
2686                 + "returns java.util.List<? extends E> instead of java.util.List<E>")
2687         .inFile(javaFileObject)
2688         .onLineContaining("ListFactory<E> blimBuilder()");
2689   }
2690 
2691   @Test
autoValueBuilderPropertyBuilderCantConstruct()2692   public void autoValueBuilderPropertyBuilderCantConstruct() {
2693     JavaFileObject javaFileObject =
2694         JavaFileObjects.forSourceLines(
2695             "foo.bar.Baz",
2696             "package foo.bar;",
2697             "",
2698             "import com.google.auto.value.AutoValue;",
2699             "import com.google.common.collect.ImmutableSet;",
2700             "",
2701             "@AutoValue",
2702             "public abstract class Baz<E> {",
2703             "  abstract String blim();",
2704             "",
2705             "  public static class StringFactory {",
2706             "    private StringFactory() {}",
2707             "",
2708             "    public String build() {",
2709             "      return null;",
2710             "    }",
2711             "  }",
2712             "",
2713             "  @AutoValue.Builder",
2714             "  public interface Builder<E> {",
2715             "    StringFactory blimBuilder();",
2716             "    Baz<E> build();",
2717             "  }",
2718             "}");
2719     Compilation compilation =
2720         javac()
2721             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2722             .compile(javaFileObject);
2723     assertThat(compilation)
2724         .hadErrorContaining(
2725             "Method looks like a property builder, but its type foo.bar.Baz.StringFactory "
2726                 + "does not have a public constructor and java.lang.String does not have a static "
2727                 + "builder() or newBuilder() method that returns foo.bar.Baz.StringFactory")
2728         .inFile(javaFileObject)
2729         .onLineContaining("StringFactory blimBuilder()");
2730   }
2731 
2732   @Test
autoValueBuilderPropertyBuilderCantReconstruct()2733   public void autoValueBuilderPropertyBuilderCantReconstruct() {
2734     JavaFileObject javaFileObject =
2735         JavaFileObjects.forSourceLines(
2736             "foo.bar.Baz",
2737             "package foo.bar;",
2738             "",
2739             "import com.google.auto.value.AutoValue;",
2740             "",
2741             "@AutoValue",
2742             "public abstract class Baz<E> {",
2743             "  abstract String blim();",
2744             "  abstract Builder<E> toBuilder();",
2745             "",
2746             "  public static class StringFactory {",
2747             "    public String build() {",
2748             "      return null;",
2749             "    }",
2750             "  }",
2751             "",
2752             "  @AutoValue.Builder",
2753             "  public interface Builder<E> {",
2754             "    StringFactory blimBuilder();",
2755             "    Baz<E> build();",
2756             "  }",
2757             "}");
2758     Compilation compilation =
2759         javac()
2760             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2761             .compile(javaFileObject);
2762     assertThat(compilation)
2763         .hadErrorContaining(
2764             "Property builder method returns foo.bar.Baz.StringFactory but there is no way to make"
2765                 + " that type from java.lang.String: java.lang.String does not have a non-static"
2766                 + " toBuilder() method that returns foo.bar.Baz.StringFactory, and"
2767                 + " foo.bar.Baz.StringFactory does not have a method addAll or putAll that accepts"
2768                 + " an argument of type java.lang.String")
2769         .inFile(javaFileObject)
2770         .onLineContaining("StringFactory blimBuilder()");
2771   }
2772 
2773   @Test
autoValueBuilderPropertyBuilderWrongTypeAddAll()2774   public void autoValueBuilderPropertyBuilderWrongTypeAddAll() {
2775     JavaFileObject javaFileObject =
2776         JavaFileObjects.forSourceLines(
2777             "foo.bar.Baz",
2778             "package foo.bar;",
2779             "",
2780             "import com.google.auto.value.AutoValue;",
2781             "import com.google.common.collect.ImmutableSet;",
2782             "import java.util.Iterator;",
2783             "",
2784             "@AutoValue",
2785             "public abstract class Baz<T> {",
2786             "  abstract ImmutableSet<String> strings();",
2787             "  abstract Builder<T> toBuilder();",
2788             "",
2789             "  public static class ImmutableSetBuilder<E> {",
2790             "    public void addAll(Iterator<? extends E> elements) {}",
2791             "",
2792             "    public ImmutableSet<E> build() {",
2793             "      return null;",
2794             "    }",
2795             "  }",
2796             "",
2797             "  @AutoValue.Builder",
2798             "  public interface Builder<T> {",
2799             "    ImmutableSetBuilder<String> stringsBuilder();",
2800             "    Baz<T> build();",
2801             "  }",
2802             "}");
2803     Compilation compilation =
2804         javac()
2805             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2806             .compile(javaFileObject);
2807     assertThat(compilation)
2808         .hadErrorContaining(
2809             "Property builder method returns foo.bar.Baz.ImmutableSetBuilder<java.lang.String> but"
2810                 + " there is no way to make that type from"
2811                 + " com.google.common.collect.ImmutableSet<java.lang.String>:"
2812                 + " com.google.common.collect.ImmutableSet<java.lang.String> does not have a"
2813                 + " non-static toBuilder() method that returns"
2814                 + " foo.bar.Baz.ImmutableSetBuilder<java.lang.String>, and"
2815                 + " foo.bar.Baz.ImmutableSetBuilder<java.lang.String> does not have a method"
2816                 + " addAll or putAll that accepts an argument of type"
2817                 + " com.google.common.collect.ImmutableSet<java.lang.String>")
2818         .inFile(javaFileObject)
2819         .onLineContaining("ImmutableSetBuilder<String> stringsBuilder();");
2820   }
2821 
2822   @Test
autoValueBuilderPropertyBuilderCantSet()2823   public void autoValueBuilderPropertyBuilderCantSet() {
2824     JavaFileObject javaFileObject =
2825         JavaFileObjects.forSourceLines(
2826             "foo.bar.Baz",
2827             "package foo.bar;",
2828             "",
2829             "import com.google.auto.value.AutoValue;",
2830             "import com.google.common.collect.ImmutableSet;",
2831             "",
2832             "@AutoValue",
2833             "public abstract class Baz<E> {",
2834             "  abstract String blim();",
2835             "",
2836             "  public static class StringFactory {",
2837             "    public String build() {",
2838             "      return null;",
2839             "    }",
2840             "  }",
2841             "",
2842             "  @AutoValue.Builder",
2843             "  public interface Builder<E> {",
2844             "    Builder<E> setBlim(String s);",
2845             "    StringFactory blimBuilder();",
2846             "    Baz<E> build();",
2847             "  }",
2848             "}");
2849     Compilation compilation =
2850         javac()
2851             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2852             .compile(javaFileObject);
2853     assertThat(compilation)
2854         .hadErrorContaining(
2855             "Property builder method returns foo.bar.Baz.StringFactory but there is no way to make "
2856                 + "that type from java.lang.String: java.lang.String does not have a non-static "
2857                 + "toBuilder() method that returns foo.bar.Baz.StringFactory")
2858         .inFile(javaFileObject)
2859         .onLineContaining("StringFactory blimBuilder()");
2860   }
2861 
2862   @Test
autoValueBuilderPropertyBuilderWrongTypeToBuilder()2863   public void autoValueBuilderPropertyBuilderWrongTypeToBuilder() {
2864     JavaFileObject javaFileObject =
2865         JavaFileObjects.forSourceLines(
2866             "foo.bar.Baz",
2867             "package foo.bar;",
2868             "",
2869             "import com.google.auto.value.AutoValue;",
2870             "import com.google.common.collect.ImmutableSet;",
2871             "",
2872             "@AutoValue",
2873             "public abstract class Baz<E> {",
2874             "  abstract Buh blim();",
2875             "  abstract Builder<E> toBuilder();",
2876             "",
2877             "  public static class Buh {",
2878             "    StringBuilder toBuilder() {",
2879             "      return null;",
2880             "    }",
2881             "  }",
2882             "",
2883             "  public static class BuhBuilder {",
2884             "    public Buh build() {",
2885             "      return null;",
2886             "    }",
2887             "  }",
2888             "",
2889             "  @AutoValue.Builder",
2890             "  public interface Builder<E> {",
2891             "    BuhBuilder blimBuilder();",
2892             "    Baz<E> build();",
2893             "  }",
2894             "}");
2895     Compilation compilation =
2896         javac()
2897             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2898             .compile(javaFileObject);
2899     assertThat(compilation)
2900         .hadErrorContaining(
2901             "Property builder method returns foo.bar.Baz.BuhBuilder but there is no way to make "
2902                 + "that type from foo.bar.Baz.Buh: foo.bar.Baz.Buh does not have a non-static "
2903                 + "toBuilder() method that returns foo.bar.Baz.BuhBuilder")
2904         .inFile(javaFileObject)
2905         .onLineContaining("BuhBuilder blimBuilder()");
2906   }
2907 
2908   @Test
autoValueBuilderPropertyBuilderWrongElementType()2909   public void autoValueBuilderPropertyBuilderWrongElementType() {
2910     JavaFileObject javaFileObject =
2911         JavaFileObjects.forSourceLines(
2912             "foo.bar.Baz",
2913             "package foo.bar;",
2914             "",
2915             "import com.google.auto.value.AutoValue;",
2916             "import com.google.common.collect.ImmutableSet;",
2917             "",
2918             "@AutoValue",
2919             "public abstract class Baz<T, U> {",
2920             "  abstract ImmutableSet<T> blim();",
2921             "",
2922             "  @AutoValue.Builder",
2923             "  public interface Builder<T, U> {",
2924             "    ImmutableSet.Builder<U> blimBuilder();",
2925             "    Baz<T, U> build();",
2926             "  }",
2927             "}");
2928     Compilation compilation =
2929         javac()
2930             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2931             .compile(javaFileObject);
2932     assertThat(compilation)
2933         .hadErrorContaining(
2934             "Property builder for blim has type com.google.common.collect.ImmutableSet.Builder "
2935                 + "whose build() method returns com.google.common.collect.ImmutableSet<U> "
2936                 + "instead of com.google.common.collect.ImmutableSet<T>")
2937         .inFile(javaFileObject)
2938         .onLineContaining("ImmutableSet.Builder<U> blimBuilder()");
2939   }
2940 
2941   @Test
autoValueBuilderAlienMethod0()2942   public void autoValueBuilderAlienMethod0() {
2943     JavaFileObject javaFileObject =
2944         JavaFileObjects.forSourceLines(
2945             "foo.bar.Baz",
2946             "package foo.bar;",
2947             "",
2948             "import com.google.auto.value.AutoValue;",
2949             "",
2950             "@AutoValue",
2951             "public abstract class Baz {",
2952             "  abstract String blam();",
2953             "",
2954             "  @AutoValue.Builder",
2955             "  public interface Builder {",
2956             "    Builder blam(String x);",
2957             "    Builder whut();",
2958             "    Baz build();",
2959             "  }",
2960             "}");
2961     Compilation compilation =
2962         javac()
2963             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2964             .compile(javaFileObject);
2965     assertThat(compilation)
2966         .hadErrorContaining(
2967             "Method without arguments should be a build method returning foo.bar.Baz, or a getter"
2968                 + " method with the same name and type as a property method of foo.bar.Baz, or"
2969                 + " fooBuilder() where foo() or getFoo() is a property method of foo.bar.Baz")
2970         .inFile(javaFileObject)
2971         .onLineContaining("Builder whut()");
2972   }
2973 
2974   @Test
autoValueBuilderAlienMethod1()2975   public void autoValueBuilderAlienMethod1() {
2976     JavaFileObject javaFileObject =
2977         JavaFileObjects.forSourceLines(
2978             "foo.bar.Baz",
2979             "package foo.bar;",
2980             "",
2981             "import com.google.auto.value.AutoValue;",
2982             "",
2983             "@AutoValue",
2984             "public abstract class Baz {",
2985             "  abstract String blam();",
2986             "",
2987             "  @AutoValue.Builder",
2988             "  public interface Builder {",
2989             "    void whut(String x);",
2990             "    Baz build();",
2991             "  }",
2992             "}");
2993     Compilation compilation =
2994         javac()
2995             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
2996             .compile(javaFileObject);
2997     assertThat(compilation)
2998         .hadErrorContaining("Method whut does not correspond to a property method of foo.bar.Baz")
2999         .inFile(javaFileObject)
3000         .onLineContaining("void whut(String x)");
3001   }
3002 
3003   @Test
autoValueBuilderAlienMethod2()3004   public void autoValueBuilderAlienMethod2() {
3005     JavaFileObject javaFileObject =
3006         JavaFileObjects.forSourceLines(
3007             "foo.bar.Baz",
3008             "package foo.bar;",
3009             "",
3010             "import com.google.auto.value.AutoValue;",
3011             "",
3012             "@AutoValue",
3013             "public abstract class Baz {",
3014             "  abstract String blam();",
3015             "",
3016             "  @AutoValue.Builder",
3017             "  public interface Builder {",
3018             "    Builder blam(String x, String y);",
3019             "    Baz build();",
3020             "  }",
3021             "}");
3022     Compilation compilation =
3023         javac()
3024             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
3025             .compile(javaFileObject);
3026     assertThat(compilation)
3027         .hadErrorContaining("Builder methods must have 0 or 1 parameters")
3028         .inFile(javaFileObject)
3029         .onLineContaining("Builder blam(String x, String y)");
3030   }
3031 
3032   @Test
autoValueBuilderMissingBuildMethod()3033   public void autoValueBuilderMissingBuildMethod() {
3034     JavaFileObject javaFileObject =
3035         JavaFileObjects.forSourceLines(
3036             "foo.bar.Baz",
3037             "package foo.bar;",
3038             "",
3039             "import com.google.auto.value.AutoValue;",
3040             "",
3041             "@AutoValue",
3042             "public abstract class Baz<T> {",
3043             "  abstract T blam();",
3044             "",
3045             "  @AutoValue.Builder",
3046             "  public interface Builder<T> {",
3047             "    Builder<T> blam(T x);",
3048             "  }",
3049             "}");
3050     Compilation compilation =
3051         javac()
3052             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
3053             .compile(javaFileObject);
3054     assertThat(compilation)
3055         .hadErrorContaining(
3056             "Builder must have a single no-argument method, typically called build(), that returns"
3057                 + " foo.bar.Baz<T>")
3058         .inFile(javaFileObject)
3059         .onLineContaining("public interface Builder<T>");
3060   }
3061 
3062   @Test
autoValueBuilderDuplicateBuildMethods()3063   public void autoValueBuilderDuplicateBuildMethods() {
3064     JavaFileObject javaFileObject =
3065         JavaFileObjects.forSourceLines(
3066             "foo.bar.Baz",
3067             "package foo.bar;",
3068             "",
3069             "import com.google.auto.value.AutoValue;",
3070             "",
3071             "@AutoValue",
3072             "public abstract class Baz {",
3073             "  abstract String blam();",
3074             "",
3075             "  @AutoValue.Builder",
3076             "  public interface Builder {",
3077             "    Builder blam(String x);",
3078             "    Baz build();",
3079             "    Baz create();",
3080             "  }",
3081             "}");
3082     Compilation compilation =
3083         javac()
3084             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
3085             .compile(javaFileObject);
3086     assertThat(compilation)
3087         .hadErrorContaining(
3088             "Builder must have a single no-argument method, typically called build(), that returns"
3089                 + " foo.bar.Baz")
3090         .inFile(javaFileObject)
3091         .onLineContaining("Baz build()");
3092     assertThat(compilation)
3093         .hadErrorContaining(
3094             "Builder must have a single no-argument method, typically called build(), that returns"
3095                 + " foo.bar.Baz")
3096         .inFile(javaFileObject)
3097         .onLineContaining("Baz create()");
3098   }
3099 
3100   @Test
autoValueBuilderWrongTypeBuildMethod()3101   public void autoValueBuilderWrongTypeBuildMethod() {
3102     JavaFileObject javaFileObject =
3103         JavaFileObjects.forSourceLines(
3104             "foo.bar.Baz",
3105             "package foo.bar;",
3106             "",
3107             "import com.google.auto.value.AutoValue;",
3108             "",
3109             "@AutoValue",
3110             "public abstract class Baz {",
3111             "  abstract String blam();",
3112             "",
3113             "  @AutoValue.Builder",
3114             "  public interface Builder {",
3115             "    Builder blam(String x);",
3116             "    String build();",
3117             "  }",
3118             "}");
3119     Compilation compilation =
3120         javac()
3121             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
3122             .compile(javaFileObject);
3123     assertThat(compilation)
3124         .hadErrorContaining(
3125             "Method without arguments should be a build method returning foo.bar.Baz")
3126         .inFile(javaFileObject)
3127         .onLineContaining("String build()");
3128   }
3129 
3130   @Test
autoValueBuilderTypeParametersDontMatch1()3131   public void autoValueBuilderTypeParametersDontMatch1() {
3132     JavaFileObject javaFileObject =
3133         JavaFileObjects.forSourceLines(
3134             "foo.bar.Baz",
3135             "package foo.bar;",
3136             "",
3137             "import com.google.auto.value.AutoValue;",
3138             "",
3139             "@AutoValue",
3140             "public abstract class Baz<T> {",
3141             "  abstract String blam();",
3142             "",
3143             "  @AutoValue.Builder",
3144             "  public interface Builder {",
3145             "    Builder blam(String x);",
3146             "    Baz build();",
3147             "  }",
3148             "}");
3149     Compilation compilation =
3150         javac()
3151             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
3152             .compile(javaFileObject);
3153     assertThat(compilation)
3154         .hadErrorContaining(
3155             "Type parameters of foo.bar.Baz.Builder must have same names and "
3156                 + "bounds as type parameters of foo.bar.Baz")
3157         .inFile(javaFileObject)
3158         .onLineContaining("public interface Builder");
3159   }
3160 
3161   @Test
autoValueBuilderTypeParametersDontMatch2()3162   public void autoValueBuilderTypeParametersDontMatch2() {
3163     JavaFileObject javaFileObject =
3164         JavaFileObjects.forSourceLines(
3165             "foo.bar.Baz",
3166             "package foo.bar;",
3167             "",
3168             "import com.google.auto.value.AutoValue;",
3169             "",
3170             "@AutoValue",
3171             "public abstract class Baz<T> {",
3172             "  abstract T blam();",
3173             "",
3174             "  @AutoValue.Builder",
3175             "  public interface Builder<E> {",
3176             "    Builder<E> blam(E x);",
3177             "    Baz build();",
3178             "  }",
3179             "}");
3180     Compilation compilation =
3181         javac()
3182             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
3183             .compile(javaFileObject);
3184     assertThat(compilation)
3185         .hadErrorContaining(
3186             "Type parameters of foo.bar.Baz.Builder must have same names and "
3187                 + "bounds as type parameters of foo.bar.Baz")
3188         .inFile(javaFileObject)
3189         .onLineContaining("public interface Builder<E>");
3190   }
3191 
3192   @Test
autoValueBuilderTypeParametersDontMatch3()3193   public void autoValueBuilderTypeParametersDontMatch3() {
3194     JavaFileObject javaFileObject =
3195         JavaFileObjects.forSourceLines(
3196             "foo.bar.Baz",
3197             "package foo.bar;",
3198             "",
3199             "import com.google.auto.value.AutoValue;",
3200             "",
3201             "@AutoValue",
3202             "public abstract class Baz<T extends Number & Comparable<T>> {",
3203             "  abstract T blam();",
3204             "",
3205             "  @AutoValue.Builder",
3206             "  public interface Builder<T extends Number> {",
3207             "    Builder<T> blam(T x);",
3208             "    Baz build();",
3209             "  }",
3210             "}");
3211     Compilation compilation =
3212         javac()
3213             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
3214             .compile(javaFileObject);
3215     assertThat(compilation)
3216         .hadErrorContaining(
3217             "Type parameters of foo.bar.Baz.Builder must have same names and "
3218                 + "bounds as type parameters of foo.bar.Baz")
3219         .inFile(javaFileObject)
3220         .onLineContaining("public interface Builder<T extends Number>");
3221   }
3222 
3223   @Test
autoValueBuilderToBuilderWrongTypeParameters()3224   public void autoValueBuilderToBuilderWrongTypeParameters() {
3225     JavaFileObject javaFileObject =
3226         JavaFileObjects.forSourceLines(
3227             "foo.bar.Baz",
3228             "package foo.bar;",
3229             "",
3230             "import com.google.auto.value.AutoValue;",
3231             "",
3232             "@AutoValue",
3233             "abstract class Baz<K extends Comparable<K>, V> {",
3234             "  abstract K key();",
3235             "  abstract V value();",
3236             "  abstract Builder<V, K> toBuilder1();",
3237             "",
3238             "  @AutoValue.Builder",
3239             "  interface Builder<K extends Comparable<K>, V> {",
3240             "    Builder<K, V> key(K key);",
3241             "    Builder<K, V> value(V value);",
3242             "    Baz<K, V> build();",
3243             "  }",
3244             "}");
3245     Compilation compilation =
3246         javac()
3247             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
3248             .compile(javaFileObject);
3249     assertThat(compilation)
3250         .hadErrorContaining("Builder converter method should return foo.bar.Baz.Builder<K, V>")
3251         .inFile(javaFileObject)
3252         .onLineContaining("abstract Builder<V, K> toBuilder1()");
3253   }
3254 
3255   @Test
autoValueBuilderToBuilderDuplicate()3256   public void autoValueBuilderToBuilderDuplicate() {
3257     JavaFileObject javaFileObject =
3258         JavaFileObjects.forSourceLines(
3259             "foo.bar.Baz",
3260             "package foo.bar;",
3261             "",
3262             "import com.google.auto.value.AutoValue;",
3263             "",
3264             "@AutoValue",
3265             "abstract class Baz<K extends Comparable<K>, V> {",
3266             "  abstract K key();",
3267             "  abstract V value();",
3268             "  abstract Builder<K, V> toBuilder1();",
3269             "  abstract Builder<K, V> toBuilder2();",
3270             "",
3271             "  @AutoValue.Builder",
3272             "  interface Builder<K extends Comparable<K>, V> {",
3273             "    Builder<K, V> key(K key);",
3274             "    Builder<K, V> value(V value);",
3275             "    Baz<K, V> build();",
3276             "  }",
3277             "}");
3278     Compilation compilation =
3279         javac()
3280             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
3281             .compile(javaFileObject);
3282     assertThat(compilation)
3283         .hadErrorContaining("There can be at most one builder converter method")
3284         .inFile(javaFileObject)
3285         .onLineContaining("abstract Builder<K, V> toBuilder1()");
3286   }
3287 
3288   @Test
getFooIsFoo()3289   public void getFooIsFoo() {
3290     JavaFileObject javaFileObject =
3291         JavaFileObjects.forSourceLines(
3292             "foo.bar.Baz",
3293             "package foo.bar;",
3294             "",
3295             "import com.google.auto.value.AutoValue;",
3296             "",
3297             "@AutoValue",
3298             "public abstract class Baz {",
3299             "  abstract int getFoo();",
3300             "  abstract boolean isFoo();",
3301             "}");
3302     Compilation compilation =
3303         javac()
3304             .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
3305             .compile(javaFileObject);
3306     assertThat(compilation)
3307         .hadErrorContaining("More than one @AutoValue property called foo")
3308         .inFile(javaFileObject)
3309         .onLineContaining("getFoo");
3310     assertThat(compilation)
3311         .hadErrorContaining("More than one @AutoValue property called foo")
3312         .inFile(javaFileObject)
3313         .onLineContaining("isFoo");
3314   }
3315 
3316   @Retention(RetentionPolicy.SOURCE)
3317   public @interface Foo {}
3318 
3319   /* Processor that generates an empty class BarFoo every time it sees a class Bar annotated with
3320    * @Foo.
3321    */
3322   public static class FooProcessor extends AbstractProcessor {
3323     @Override
getSupportedAnnotationTypes()3324     public Set<String> getSupportedAnnotationTypes() {
3325       return ImmutableSet.of(Foo.class.getCanonicalName());
3326     }
3327 
3328     @Override
getSupportedSourceVersion()3329     public SourceVersion getSupportedSourceVersion() {
3330       return SourceVersion.latestSupported();
3331     }
3332 
3333     @Override
process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)3334     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
3335       Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Foo.class);
3336       for (TypeElement type : ElementFilter.typesIn(elements)) {
3337         try {
3338           generateFoo(type);
3339         } catch (IOException e) {
3340           throw new AssertionError(e);
3341         }
3342       }
3343       return false;
3344     }
3345 
generateFoo(TypeElement type)3346     private void generateFoo(TypeElement type) throws IOException {
3347       String pkg = TypeSimplifier.packageNameOf(type);
3348       String className = type.getSimpleName().toString();
3349       String generatedClassName = className + "Foo";
3350       JavaFileObject source =
3351           processingEnv.getFiler().createSourceFile(pkg + "." + generatedClassName, type);
3352       PrintWriter writer = new PrintWriter(source.openWriter());
3353       writer.println("package " + pkg + ";");
3354       writer.println("public class " + generatedClassName + " {}");
3355       writer.close();
3356     }
3357   }
3358 
3359   @Test
referencingGeneratedClass()3360   public void referencingGeneratedClass() {
3361     // Test that ensures that a type that does not exist can be the type of an @AutoValue property
3362     // as long as it later does come into existence. The BarFoo type referenced here does not exist
3363     // when the AutoValueProcessor runs on the first round, but the FooProcessor then generates it.
3364     // That generation provokes a further round of annotation processing and AutoValueProcessor
3365     // should succeed then.
3366     JavaFileObject bazFileObject =
3367         JavaFileObjects.forSourceLines(
3368             "foo.bar.Baz",
3369             "package foo.bar;",
3370             "",
3371             "import com.google.auto.value.AutoValue;",
3372             "",
3373             "@AutoValue",
3374             "public abstract class Baz {",
3375             "  public abstract BarFoo barFoo();",
3376             "",
3377             "  public static Baz create(BarFoo barFoo) {",
3378             "    return new AutoValue_Baz(barFoo);",
3379             "  }",
3380             "}");
3381     JavaFileObject barFileObject =
3382         JavaFileObjects.forSourceLines(
3383             "foo.bar.Bar",
3384             "package foo.bar;",
3385             "",
3386             "@" + Foo.class.getCanonicalName(),
3387             "public abstract class Bar {",
3388             "  public abstract BarFoo barFoo();",
3389             "}");
3390     Compilation compilation =
3391         javac()
3392             .withProcessors(new AutoValueProcessor(), new FooProcessor())
3393             .withOptions("-Xlint:-processing", "-implicit:none")
3394             .compile(bazFileObject, barFileObject);
3395     assertThat(compilation).succeededWithoutWarnings();
3396   }
3397 
3398   @Test
referencingGeneratedClassInAnnotation()3399   public void referencingGeneratedClassInAnnotation() {
3400     // Test that ensures that a type that does not exist can be referenced by a copied annotation
3401     // as long as it later does come into existence. The BarFoo type referenced here does not exist
3402     // when the AutoValueProcessor runs on the first round, but the FooProcessor then generates it.
3403     // That generation provokes a further round of annotation processing and AutoValueProcessor
3404     // should succeed then.
3405     // We test the three places that a class reference could appear: as the value of a Class
3406     // element, as the value of a Class[] element, in a nested annotation.
3407     JavaFileObject barFileObject =
3408         JavaFileObjects.forSourceLines(
3409             "foo.bar.Bar",
3410             "package foo.bar;",
3411             "",
3412             "@" + Foo.class.getCanonicalName(),
3413             "public abstract class Bar {",
3414             "}");
3415     JavaFileObject referenceClassFileObject =
3416         JavaFileObjects.forSourceLines(
3417             "foo.bar.ReferenceClass",
3418             "package foo.bar;",
3419             "",
3420             "@interface ReferenceClass {",
3421             "  Class<?> value() default Void.class;",
3422             "  Class<?>[] values() default {};",
3423             "  Nested nested() default @Nested;",
3424             "  @interface Nested {",
3425             "    Class<?>[] values() default {};",
3426             "  }",
3427             "}");
3428     ImmutableList<String> annotations = ImmutableList.of(
3429         "@ReferenceClass(BarFoo.class)",
3430         "@ReferenceClass(values = {Void.class, BarFoo.class})",
3431         "@ReferenceClass(nested = @ReferenceClass.Nested(values = {Void.class, BarFoo.class}))");
3432     for (String annotation : annotations) {
3433       JavaFileObject bazFileObject =
3434           JavaFileObjects.forSourceLines(
3435               "foo.bar.Baz",
3436               "package foo.bar;",
3437               "",
3438               "import com.google.auto.value.AutoValue;",
3439               "",
3440               "@AutoValue",
3441               "@AutoValue.CopyAnnotations",
3442               annotation,
3443               "public abstract class Baz {",
3444               "  public abstract int foo();",
3445               "",
3446               "  public static Baz create(int foo) {",
3447               "    return new AutoValue_Baz(foo);",
3448               "  }",
3449               "}");
3450       Compilation compilation =
3451           javac()
3452               .withProcessors(new AutoValueProcessor(), new FooProcessor())
3453               .withOptions("-Xlint:-processing", "-implicit:none")
3454               .compile(bazFileObject, barFileObject, referenceClassFileObject);
3455       expect.about(compilations()).that(compilation).succeededWithoutWarnings();
3456       if (compilation.status().equals(Compilation.Status.SUCCESS)) {
3457         expect.about(compilations()).that(compilation)
3458             .generatedSourceFile("foo.bar.AutoValue_Baz")
3459             .contentsAsUtf8String()
3460             .contains(annotation);
3461       }
3462     }
3463   }
3464 
3465   @Test
annotationReferencesUndefined()3466   public void annotationReferencesUndefined() {
3467     // Test that we don't throw an exception if asked to compile @SuppressWarnings(UNDEFINED)
3468     // where UNDEFINED is an undefined symbol.
3469     JavaFileObject bazFileObject =
3470         JavaFileObjects.forSourceLines(
3471             "foo.bar.Baz",
3472             "package foo.bar;",
3473             "",
3474             "import com.google.auto.value.AutoValue;",
3475             "",
3476             "@AutoValue",
3477             "public abstract class Baz {",
3478             "  @SuppressWarnings(UNDEFINED)",
3479             "  public abstract int[] buh();",
3480             "}");
3481     Compilation compilation1 =
3482         javac()
3483             .withOptions("-Xlint:-processing")
3484             .withProcessors(new AutoValueProcessor())
3485             .compile(bazFileObject);
3486     assertThat(compilation1).hadErrorCount(1);
3487     assertThat(compilation1)
3488         .hadErrorContaining("UNDEFINED")
3489         .inFile(bazFileObject)
3490         .onLineContaining("UNDEFINED");
3491     assertThat(compilation1).hadWarningCount(1);
3492     assertThat(compilation1)
3493         .hadWarningContaining("mutable")
3494         .inFile(bazFileObject)
3495         .onLineContaining("public abstract int[] buh()");
3496 
3497     // Same test, except we do successfully suppress the warning despite the UNDEFINED.
3498     bazFileObject =
3499         JavaFileObjects.forSourceLines(
3500             "foo.bar.Baz",
3501             "package foo.bar;",
3502             "",
3503             "import com.google.auto.value.AutoValue;",
3504             "",
3505             "@AutoValue",
3506             "public abstract class Baz {",
3507             "  @SuppressWarnings({UNDEFINED, \"mutable\"})",
3508             "  public abstract int[] buh();",
3509             "}");
3510     Compilation compilation2 =
3511         javac()
3512             .withOptions("-Xlint:-processing")
3513             .withProcessors(new AutoValueProcessor())
3514             .compile(bazFileObject);
3515     assertThat(compilation2).hadErrorCount(1);
3516     assertThat(compilation2)
3517         .hadErrorContaining("UNDEFINED")
3518         .inFile(bazFileObject)
3519         .onLineContaining("UNDEFINED");
3520     assertThat(compilation2).hadWarningCount(0);
3521   }
3522 
3523   @Test
packagePrivateAnnotationFromOtherPackage()3524   public void packagePrivateAnnotationFromOtherPackage() {
3525     JavaFileObject bazFileObject =
3526         JavaFileObjects.forSourceLines(
3527             "foo.bar.Baz",
3528             "package foo.bar;",
3529             "",
3530             "import com.google.auto.value.AutoValue;",
3531             "",
3532             "@AutoValue",
3533             "public abstract class Baz extends otherpackage.Parent {",
3534             "}");
3535     JavaFileObject parentFileObject =
3536         JavaFileObjects.forSourceLines(
3537             "otherpackage.Parent",
3538             "package otherpackage;",
3539             "",
3540             "public abstract class Parent {",
3541             "  @PackageAnnotation",
3542             "  public abstract String foo();",
3543             "",
3544             "  @interface PackageAnnotation {}",
3545             "}");
3546     Compilation compilation =
3547         javac()
3548             .withProcessors(new AutoValueProcessor())
3549             .withOptions("-Xlint:-processing", "-implicit:none")
3550             .compile(bazFileObject, parentFileObject);
3551     assertThat(compilation).succeededWithoutWarnings();
3552     assertThat(compilation).generatedSourceFile("foo.bar.AutoValue_Baz");
3553   }
3554 
3555   @Test
visibleProtectedAnnotationFromOtherPackage()3556   public void visibleProtectedAnnotationFromOtherPackage() {
3557     JavaFileObject bazFileObject =
3558         JavaFileObjects.forSourceLines(
3559             "foo.bar.Baz",
3560             "package foo.bar;",
3561             "",
3562             "import com.google.auto.value.AutoValue;",
3563             "",
3564             "@AutoValue",
3565             "public abstract class Baz extends otherpackage.Parent {}");
3566     JavaFileObject parentFileObject =
3567         JavaFileObjects.forSourceLines(
3568             "otherpackage.Parent",
3569             "package otherpackage;",
3570             "",
3571             "public abstract class Parent {",
3572             "  @ProtectedAnnotation",
3573             "  public abstract String foo();",
3574             "",
3575             "  protected @interface ProtectedAnnotation {}",
3576             "}");
3577     Compilation compilation =
3578         javac()
3579             .withProcessors(new AutoValueProcessor())
3580             .withOptions("-Xlint:-processing", "-implicit:none")
3581             .compile(bazFileObject, parentFileObject);
3582     assertThat(compilation).succeededWithoutWarnings();
3583     assertThat(compilation)
3584         .generatedSourceFile("foo.bar.AutoValue_Baz")
3585         .contentsAsUtf8String()
3586         .containsMatch("(?s:@Parent.ProtectedAnnotation\\s*@Override\\s*public String foo\\(\\))");
3587   }
3588 
3589   @Test
methodAnnotationsCopiedInLexicographicalOrder()3590   public void methodAnnotationsCopiedInLexicographicalOrder() {
3591     JavaFileObject bazFileObject =
3592         JavaFileObjects.forSourceLines(
3593             "foo.bar.Baz",
3594             "package foo.bar;",
3595             "",
3596             "import com.google.auto.value.AutoValue;",
3597             "import com.package1.Annotation1;",
3598             "import com.package2.Annotation0;",
3599             "",
3600             "@AutoValue",
3601             "public abstract class Baz extends Parent {",
3602             "  @Annotation0",
3603             "  @Annotation1",
3604             "  @Override",
3605             "  public abstract String foo();",
3606             "}");
3607     JavaFileObject parentFileObject =
3608         JavaFileObjects.forSourceLines(
3609             "foo.bar.Parent",
3610             "package foo.bar;",
3611             "",
3612             "public abstract class Parent {",
3613             "  public abstract String foo();",
3614             "}");
3615     JavaFileObject annotation1FileObject =
3616         JavaFileObjects.forSourceLines(
3617             "com.package1.Annotation1",
3618             "package com.package1;",
3619             "",
3620             "import java.lang.annotation.ElementType;",
3621             "import java.lang.annotation.Target;",
3622             "",
3623             "@Target({ElementType.FIELD, ElementType.METHOD})",
3624             "public @interface Annotation1 {}");
3625     JavaFileObject annotation0FileObject =
3626         JavaFileObjects.forSourceLines(
3627             "com.package2.Annotation0",
3628             "package com.package2;",
3629             "",
3630             "public @interface Annotation0 {}");
3631     Compilation compilation =
3632         javac()
3633             .withProcessors(new AutoValueProcessor())
3634             .withOptions("-Xlint:-processing", "-implicit:none")
3635             .compile(bazFileObject, parentFileObject, annotation1FileObject, annotation0FileObject);
3636     assertThat(compilation).succeededWithoutWarnings();
3637     assertThat(compilation)
3638         .generatedSourceFile("foo.bar.AutoValue_Baz")
3639         .contentsAsUtf8String()
3640         .containsMatch(
3641             "(?s:@Annotation1\\s+@Annotation0\\s+@Override\\s+public String foo\\(\\))");
3642     // @Annotation1 precedes @Annotation 0 because
3643     // @com.package2.Annotation1 precedes @com.package1.Annotation0
3644   }
3645 
3646   @Test
nonVisibleProtectedAnnotationFromOtherPackage()3647   public void nonVisibleProtectedAnnotationFromOtherPackage() {
3648     JavaFileObject bazFileObject =
3649         JavaFileObjects.forSourceLines(
3650             "foo.bar.Baz",
3651             "package foo.bar;",
3652             "",
3653             "import com.google.auto.value.AutoValue;",
3654             "",
3655             "@AutoValue",
3656             "public abstract class Baz extends otherpackage.Parent {",
3657             "}");
3658     JavaFileObject parentFileObject =
3659         JavaFileObjects.forSourceLines(
3660             "otherpackage.Parent",
3661             "package otherpackage;",
3662             "",
3663             "import otherpackage.Annotations.ProtectedAnnotation;",
3664             "",
3665             "public abstract class Parent {",
3666             "  @ProtectedAnnotation",
3667             "  public abstract String foo();",
3668             "}");
3669     JavaFileObject annotationsFileObject =
3670         JavaFileObjects.forSourceLines(
3671             "otherpackage.Annotations",
3672             "package otherpackage;",
3673             "",
3674             "public class Annotations {",
3675             "  protected @interface ProtectedAnnotation {}",
3676             "}");
3677     Compilation compilation =
3678         javac()
3679             .withProcessors(new AutoValueProcessor())
3680             .withOptions("-Xlint:-processing", "-implicit:none")
3681             .compile(bazFileObject, parentFileObject, annotationsFileObject);
3682     assertThat(compilation).succeededWithoutWarnings();
3683     assertThat(compilation)
3684         .generatedSourceFile("foo.bar.AutoValue_Baz")
3685         .contentsAsUtf8String()
3686         .doesNotContain("ProtectedAnnotation");
3687   }
3688 
3689   @Test
nonVisibleProtectedClassAnnotationFromOtherPackage()3690   public void nonVisibleProtectedClassAnnotationFromOtherPackage() {
3691     JavaFileObject bazFileObject =
3692         JavaFileObjects.forSourceLines(
3693             "foo.bar.Outer",
3694             "package foo.bar;",
3695             "",
3696             "import com.google.auto.value.AutoValue;",
3697             "",
3698             "class Outer extends otherpackage.Parent {",
3699             "  @AutoValue",
3700             "  @AutoValue.CopyAnnotations",
3701             "  @ProtectedAnnotation",
3702             "  abstract static class Inner {",
3703             "    abstract String foo();",
3704             "  }",
3705             "}");
3706     JavaFileObject parentFileObject =
3707         JavaFileObjects.forSourceLines(
3708             "otherpackage.Parent",
3709             "package otherpackage;",
3710             "",
3711             "public abstract class Parent {",
3712             "  protected @interface ProtectedAnnotation {}",
3713             "}");
3714     Compilation compilation =
3715         javac()
3716             .withProcessors(new AutoValueProcessor())
3717             .withOptions("-Xlint:-processing", "-implicit:none")
3718             .compile(bazFileObject, parentFileObject);
3719     assertThat(compilation).succeededWithoutWarnings();
3720     assertThat(compilation)
3721         .generatedSourceFile("foo.bar.AutoValue_Outer_Inner")
3722         .contentsAsUtf8String()
3723         .doesNotContain("ProtectedAnnotation");
3724   }
3725 
3726   @Test
builderWithVarArgsDoesNotImportJavaUtilArrays()3727   public void builderWithVarArgsDoesNotImportJavaUtilArrays() {
3728     // Repro from https://github.com/google/auto/issues/373.
3729     JavaFileObject testFileObject =
3730         JavaFileObjects.forSourceLines(
3731             "foo.bar.Test",
3732             "package foo.bar;",
3733             "",
3734             "import com.google.auto.value.AutoValue;",
3735             "import com.google.common.collect.ImmutableList;",
3736             "",
3737             "@AutoValue",
3738             "public abstract class Test {",
3739             "  abstract ImmutableList<String> foo();",
3740             "",
3741             "  @AutoValue.Builder",
3742             "  abstract static class Builder {",
3743             "    abstract Builder foo(String... foos);",
3744             "    abstract Test build();",
3745             "  }",
3746             "}");
3747     Compilation compilation =
3748         javac()
3749             .withProcessors(new AutoValueProcessor())
3750             .withOptions("-Xlint:-processing", "-implicit:none")
3751             .compile(testFileObject);
3752     assertThat(compilation).succeededWithoutWarnings();
3753     assertThat(compilation)
3754         .generatedSourceFile("foo.bar.AutoValue_Test")
3755         .contentsAsUtf8String()
3756         .doesNotContain("java.util.Arrays");
3757   }
3758 
3759   @Test
staticBuilderMethodInBuilderClass()3760   public void staticBuilderMethodInBuilderClass() {
3761     JavaFileObject javaFileObject =
3762         JavaFileObjects.forSourceLines(
3763             "com.example.Foo",
3764             "package com.example;",
3765             "",
3766             "import com.google.auto.value.AutoValue;",
3767             "",
3768             "@AutoValue",
3769             "public abstract class Foo {",
3770             "  public abstract String bar();",
3771             "",
3772             "  @AutoValue.Builder",
3773             "  public abstract static class Builder {",
3774             "    public static Builder builder() {",
3775             "      return new AutoValue_Foo.Builder();",
3776             "    }",
3777             "",
3778             "    public abstract Builder setBar(String s);",
3779             "    public abstract Foo build();",
3780             "  }",
3781             "}");
3782     Compilation compilation =
3783         javac()
3784             .withProcessors(new AutoValueProcessor())
3785             .withOptions("-Xlint:-processing", "-implicit:none")
3786             .compile(javaFileObject);
3787     assertThat(compilation).succeeded();
3788     assertThat(compilation)
3789         .hadWarningContaining("Static builder() method should be in the containing class")
3790         .inFile(javaFileObject)
3791         .onLineContaining("builder()");
3792   }
3793 
3794   /**
3795    * Tests behaviour when the package containing an {@code @AutoValue} class also has classes with
3796    * the same name as classes in {@code java.lang}. If you call a class {@code Object} you are
3797    * asking for trouble, but you could innocently call a class {@code Compiler} without realizing
3798    * there is a {@code java.lang.Compiler}.
3799    *
3800    * <p>The case where the class in question is mentioned in the {@code @AutoValue} class is the
3801    * easy one, because then our logic can easily see that there is a clash and will use
3802    * fully-qualified names. This is the case of the {@code Compiler} class below. The case where the
3803    * class is <i>not</i> mentioned is harder. We have to realize that we can't elide the package
3804    * name in {@code java.lang.Object} because there is also a {@code foo.bar.Object} in scope, and
3805    * in fact it takes precedence.
3806    */
3807   @Test
javaLangClash()3808   public void javaLangClash() {
3809     JavaFileObject object =
3810         JavaFileObjects.forSourceLines(
3811             "foo.bar.Object", //
3812             "package foo.bar;",
3813             "",
3814             "public class Object {}");
3815     JavaFileObject string =
3816         JavaFileObjects.forSourceLines(
3817             "foo.bar.String", //
3818             "package foo.bar;",
3819             "",
3820             "public class String {}");
3821     JavaFileObject integer =
3822         JavaFileObjects.forSourceLines(
3823             "foo.bar.Integer", //
3824             "package foo.bar;",
3825             "",
3826             "public class Integer {}");
3827     JavaFileObject thread =
3828         JavaFileObjects.forSourceLines(
3829             "foo.bar.Thread", //
3830             "package foo.bar;",
3831             "",
3832             "public class Thread {}");
3833     JavaFileObject override =
3834         JavaFileObjects.forSourceLines(
3835             "foo.bar.Override", //
3836             "package foo.bar;",
3837             "",
3838             "public class Override {}");
3839     JavaFileObject test =
3840         JavaFileObjects.forSourceLines(
3841             "foo.bar.Test",
3842             "package foo.bar;",
3843             "",
3844             "import com.google.auto.value.AutoValue;",
3845             "",
3846             "@AutoValue",
3847             "public abstract class Test {",
3848             "  public abstract java.lang.Integer integer();",
3849             "  public abstract java.lang.Thread.State state();",
3850             "  public static Builder builder() {",
3851             "    return new AutoValue_Test.Builder();",
3852             "  }",
3853             "",
3854             "  @AutoValue.Builder",
3855             "  public abstract static class Builder {",
3856             "    public abstract Builder setInteger(java.lang.Integer x);",
3857             "    public abstract Builder setState(java.lang.Thread.State x);",
3858             "    public abstract Test build();",
3859             "  }",
3860             "}");
3861     Compilation compilation =
3862         javac()
3863             .withProcessors(new AutoValueProcessor())
3864             .withOptions("-Xlint:-processing", "-implicit:none")
3865             .compile(object, string, integer, thread, override, test);
3866     assertThat(compilation).succeededWithoutWarnings();
3867   }
3868 
3869   // This is a regression test for the problem described in
3870   // https://github.com/google/auto/issues/847#issuecomment-629857642.
3871   @Test
generatedParentWithGeneratedGetterButSetterInBuilder()3872   public void generatedParentWithGeneratedGetterButSetterInBuilder() {
3873     JavaFileObject test =
3874         JavaFileObjects.forSourceLines(
3875             "foo.bar.Test",
3876             "package foo.bar;",
3877             "",
3878             "import com.google.auto.value.AutoValue;",
3879             "import foo.baz.GeneratedParent;",
3880             "import foo.baz.GeneratedPropertyType;",
3881             "import java.util.Optional;",
3882             "",
3883             "@AutoValue",
3884             "public abstract class Test extends GeneratedParent {",
3885             "  public abstract String string();",
3886             "",
3887             "  public static Builder builder() {",
3888             "    return new AutoValue_Test.Builder();",
3889             "  }",
3890             "",
3891             "  @AutoValue.Builder",
3892             "  public abstract static class Builder extends GeneratedParent.Builder<Builder> {",
3893             "    public abstract Builder setString(String x);",
3894             "    public abstract Builder setGenerated(GeneratedPropertyType x);",
3895             "    public abstract Test build();",
3896             "  }",
3897             "}");
3898     AutoValueProcessor autoValueProcessor = new AutoValueProcessor();
3899     GeneratedParentProcessor generatedParentProcessor =
3900         new GeneratedParentProcessor(autoValueProcessor, expect);
3901     Compilation compilation =
3902         javac()
3903             .withProcessors(autoValueProcessor, generatedParentProcessor)
3904             .withOptions("-Xlint:-processing", "-implicit:none")
3905             .compile(test);
3906     assertThat(compilation).succeededWithoutWarnings();
3907     assertThat(compilation)
3908         .generatedSourceFile("foo.bar.AutoValue_Test")
3909         .contentsAsUtf8String()
3910         .contains("  public int integer() {");
3911   }
3912 
3913   @SupportedAnnotationTypes("*")
3914   private static class GeneratedParentProcessor extends AbstractProcessor {
3915     private static final String GENERATED_PARENT =
3916         String.join(
3917             "\n",
3918             "package foo.baz;",
3919             "",
3920             "public abstract class GeneratedParent {",
3921             "  public abstract int integer();",
3922             "  public abstract GeneratedPropertyType generated();",
3923             "",
3924             "  public abstract static class Builder<B extends Builder<B>> {",
3925             "    public abstract B setInteger(int x);",
3926             "  }",
3927             "}");
3928     private static final String GENERATED_PROPERTY_TYPE =
3929         String.join(
3930             "\n", //
3931             "package foo.baz;",
3932             "",
3933             "public class GeneratedPropertyType {}");
3934     private static final ImmutableMap<String, String> GENERATED_TYPES =
3935         ImmutableMap.of(
3936             "foo.baz.GeneratedParent", GENERATED_PARENT,
3937             "foo.baz.GeneratedPropertyType", GENERATED_PROPERTY_TYPE);
3938 
3939     private final AutoValueProcessor autoValueProcessor;
3940     private final Expect expect;
3941 
GeneratedParentProcessor(AutoValueProcessor autoValueProcessor, Expect expect)3942     GeneratedParentProcessor(AutoValueProcessor autoValueProcessor, Expect expect) {
3943       this.autoValueProcessor = autoValueProcessor;
3944       this.expect = expect;
3945     }
3946 
3947     private boolean generated;
3948 
3949     @Override
process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)3950     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
3951       if (!generated) {
3952         generated = true;
3953         // Check that AutoValueProcessor has already run and deferred the foo.bar.Test type because
3954         // we haven't generated its parent yet.
3955         expect.that(autoValueProcessor.deferredTypeNames()).contains("foo.bar.Test");
3956         GENERATED_TYPES.forEach(
3957             (typeName, source) -> {
3958               try {
3959                 JavaFileObject generated = processingEnv.getFiler().createSourceFile(typeName);
3960                 try (Writer writer = generated.openWriter()) {
3961                   writer.write(source);
3962                 }
3963               } catch (IOException e) {
3964                 throw new UncheckedIOException(e);
3965               }
3966             });
3967       }
3968       return false;
3969     }
3970 
3971     @Override
getSupportedSourceVersion()3972     public SourceVersion getSupportedSourceVersion() {
3973       return SourceVersion.latestSupported();
3974     }
3975   }
3976 
3977   // This is a regression test for the problem described in
3978   // https://github.com/google/auto/issues/1087.
3979   @Test
kotlinMetadataAnnotationsAreImplicitlyExcludedFromCopying()3980   public void kotlinMetadataAnnotationsAreImplicitlyExcludedFromCopying() {
3981     JavaFileObject metadata =
3982         JavaFileObjects.forSourceLines(
3983             "kotlin.Metadata", "package kotlin;", "", "public @interface Metadata {", "}");
3984     JavaFileObject test =
3985         JavaFileObjects.forSourceLines(
3986             "foo.bar.Test",
3987             "package foo.bar;",
3988             "",
3989             "import com.google.auto.value.AutoValue;",
3990             "import kotlin.Metadata;",
3991             "",
3992             "@AutoValue.CopyAnnotations",
3993             "@Metadata",
3994             "@AutoValue",
3995             "public abstract class Test {",
3996             "  public abstract String string();",
3997             "}");
3998     AutoValueProcessor autoValueProcessor = new AutoValueProcessor();
3999     Compilation compilation =
4000         javac()
4001             .withProcessors(autoValueProcessor)
4002             .withOptions("-Xlint:-processing", "-implicit:none")
4003             .compile(test, metadata);
4004     assertThat(compilation).succeededWithoutWarnings();
4005     assertThat(compilation)
4006         .generatedSourceFile("foo.bar.AutoValue_Test")
4007         .contentsAsUtf8String()
4008         .doesNotContain("kotlin.Metadata");
4009   }
4010 
sorted(String... imports)4011   private static String sorted(String... imports) {
4012     return stream(imports).sorted().collect(joining("\n"));
4013   }
4014 }
4015