• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Dagger Authors.
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 
17 package dagger.internal.codegen;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.assertThrows;
21 
22 import androidx.room.compiler.processing.XConstructorElement;
23 import androidx.room.compiler.processing.XElement;
24 import androidx.room.compiler.processing.XMethodElement;
25 import androidx.room.compiler.processing.XProcessingEnv;
26 import androidx.room.compiler.processing.XProcessingStep;
27 import androidx.room.compiler.processing.XTypeElement;
28 import androidx.room.compiler.processing.XVariableElement;
29 import androidx.room.compiler.processing.util.Source;
30 import com.google.common.base.Joiner;
31 import com.google.common.collect.ImmutableList;
32 import com.google.common.collect.ImmutableSet;
33 import dagger.BindsInstance;
34 import dagger.Component;
35 import dagger.internal.codegen.base.DaggerSuperficialValidation;
36 import dagger.internal.codegen.base.DaggerSuperficialValidation.ValidationException;
37 import dagger.testing.compile.CompilerTests;
38 import java.util.Map;
39 import java.util.Set;
40 import javax.inject.Singleton;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 import org.junit.runners.Parameterized;
44 import org.junit.runners.Parameterized.Parameters;
45 
46 @RunWith(Parameterized.class)
47 public class DaggerSuperficialValidationTest {
48   enum SourceKind {
49     JAVA,
50     KOTLIN
51   }
52 
53   @Parameters(name = "sourceKind={0}")
parameters()54   public static ImmutableList<Object[]> parameters() {
55     return ImmutableList.of(new Object[] {SourceKind.JAVA}, new Object[] {SourceKind.KOTLIN});
56   }
57 
58   private final SourceKind sourceKind;
59 
DaggerSuperficialValidationTest(SourceKind sourceKind)60   public DaggerSuperficialValidationTest(SourceKind sourceKind) {
61     this.sourceKind = sourceKind;
62   }
63 
64   private static final Joiner NEW_LINES = Joiner.on("\n  ");
65 
66   @Test
missingReturnType()67   public void missingReturnType() {
68     runTest(
69         CompilerTests.javaSource(
70             "test.TestClass",
71             "package test;",
72             "",
73             "abstract class TestClass {",
74             "  abstract MissingType blah();",
75             "}"),
76         CompilerTests.kotlinSource(
77             "test.TestClass.kt",
78             "package test",
79             "",
80             "abstract class TestClass {",
81             "  abstract fun blah(): MissingType",
82             "}"),
83         (processingEnv, superficialValidation) -> {
84           XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
85           ValidationException exception =
86               assertThrows(
87                   ValidationException.KnownErrorType.class,
88                   () -> superficialValidation.validateElement(testClassElement));
89           // TODO(b/248552462): Javac and KSP should match once this bug is fixed.
90           boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC;
91           assertThat(exception)
92               .hasMessageThat()
93               .contains(
94                   String.format(
95                       NEW_LINES.join(
96                           "Validation trace:",
97                           "  => element (CLASS): test.TestClass",
98                           "  => element (METHOD): blah()",
99                           "  => type (ERROR return type): %1$s"),
100                       isJavac ? "MissingType" : "error.NonExistentClass"));
101         });
102   }
103 
104   @Test
missingGenericReturnType()105   public void missingGenericReturnType() {
106     runTest(
107         CompilerTests.javaSource(
108             "test.TestClass",
109             "package test;",
110             "",
111             "abstract class TestClass {",
112             "  abstract MissingType<?> blah();",
113             "}"),
114         CompilerTests.kotlinSource(
115             "test.TestClass.kt",
116             "package test",
117             "",
118             "abstract class TestClass {",
119             "  abstract fun blah(): MissingType<*>",
120             "}"),
121         (processingEnv, superficialValidation) -> {
122           XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
123           ValidationException exception =
124               assertThrows(
125                   ValidationException.KnownErrorType.class,
126                   () -> superficialValidation.validateElement(testClassElement));
127           final String errorType;
128           if (processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC) {
129             // JDK 24 improves error type information.
130             errorType =
131                 Runtime.version().feature() >= 24
132                     ? isKAPT(processingEnv) ? "MissingType" : "MissingType<?>"
133                     : "<any>";
134           } else {
135             // TODO(b/248552462): Javac and KSP should match once this bug is fixed.
136             errorType = "error.NonExistentClass";
137           }
138           assertThat(exception)
139               .hasMessageThat()
140               .contains(
141                   String.format(
142                       NEW_LINES.join(
143                           "Validation trace:",
144                           "  => element (CLASS): test.TestClass",
145                           "  => element (METHOD): blah()",
146                           "  => type (ERROR return type): %1$s"),
147                       errorType));
148         });
149   }
150 
151   @Test
missingReturnTypeTypeParameter()152   public void missingReturnTypeTypeParameter() {
153     runTest(
154         CompilerTests.javaSource(
155             "test.TestClass",
156             "package test;",
157             "",
158             "import java.util.Map;",
159             "import java.util.Set;",
160             "",
161             "abstract class TestClass {",
162             "  abstract Map<Set<?>, MissingType<?>> blah();",
163             "}"),
164         CompilerTests.kotlinSource(
165             "test.TestClass.kt",
166             "package test",
167             "",
168             "abstract class TestClass {",
169             "  abstract fun blah(): Map<Set<*>, MissingType<*>>",
170             "}"),
171         (processingEnv, superficialValidation) -> {
172           XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
173           ValidationException exception =
174               assertThrows(
175                   ValidationException.KnownErrorType.class,
176                   () -> superficialValidation.validateElement(testClassElement));
177           final String errorType;
178           if (processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC) {
179             // JDK 24 improves error type information.
180             errorType = Runtime.version().feature() >= 24 ? "MissingType<?>" : "<any>";
181           } else {
182             // TODO(b/248552462): Javac and KSP should match once this bug is fixed.
183             errorType = "error.NonExistentClass";
184           }
185           assertThat(exception)
186               .hasMessageThat()
187               .contains(
188                   String.format(
189                       NEW_LINES.join(
190                           "Validation trace:",
191                           "  => element (CLASS): test.TestClass",
192                           "  => element (METHOD): blah()",
193                           "  => type (DECLARED return type): "
194                               + "java.util.Map<java.util.Set<?>,%1$s>",
195                           "  => type (ERROR type argument): %1$s"),
196                       errorType));
197         });
198   }
199 
200   @Test
missingTypeParameter()201   public void missingTypeParameter() {
202     runTest(
203         CompilerTests.javaSource(
204             "test.TestClass", //
205             "package test;",
206             "",
207             "class TestClass<T extends MissingType> {}"),
208         CompilerTests.kotlinSource(
209             "test.TestClass.kt", //
210             "package test",
211             "",
212             "class TestClass<T : MissingType>"),
213         (processingEnv, superficialValidation) -> {
214           if (isKAPT(processingEnv)) {
215             // The KAPT java stub doesn't reference the MissingType symbol (b/268536260#comment2).
216             return;
217           }
218           XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
219           ValidationException exception =
220               assertThrows(
221                   ValidationException.KnownErrorType.class,
222                   () -> superficialValidation.validateElement(testClassElement));
223           // TODO(b/248552462): Javac and KSP should match once this bug is fixed.
224           boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC;
225           assertThat(exception)
226               .hasMessageThat()
227               .contains(
228                   String.format(
229                       NEW_LINES.join(
230                           "Validation trace:",
231                           "  => element (CLASS): test.TestClass",
232                           "  => element (TYPE_PARAMETER): T",
233                           "  => type (ERROR bound type): %s"),
234                       isJavac ? "MissingType" : "error.NonExistentClass"));
235         });
236   }
237 
238   @Test
missingParameterType()239   public void missingParameterType() {
240     runTest(
241         CompilerTests.javaSource(
242             "test.TestClass",
243             "package test;",
244             "",
245             "abstract class TestClass {",
246             "  abstract void foo(MissingType param);",
247             "}"),
248         CompilerTests.kotlinSource(
249             "test.TestClass.kt",
250             "package test",
251             "",
252             "abstract class TestClass {",
253             "  abstract fun foo(param: MissingType);",
254             "}"),
255         (processingEnv, superficialValidation) -> {
256           XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
257           ValidationException exception =
258               assertThrows(
259                   ValidationException.KnownErrorType.class,
260                   () -> superficialValidation.validateElement(testClassElement));
261           // TODO(b/248552462): Javac and KSP should match once this bug is fixed.
262           boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC;
263           assertThat(exception)
264               .hasMessageThat()
265               .contains(
266                   String.format(
267                       NEW_LINES.join(
268                           "Validation trace:",
269                           "  => element (CLASS): test.TestClass",
270                           "  => element (METHOD): foo(%1$s)",
271                           "  => element (PARAMETER): param",
272                           "  => type (ERROR parameter type): %1$s"),
273                       isJavac ? "MissingType" : "error.NonExistentClass"));
274         });
275   }
276 
277   @Test
missingAnnotation()278   public void missingAnnotation() {
279     runTest(
280         CompilerTests.javaSource(
281             "test.TestClass", //
282             "package test;",
283             "",
284             "@MissingAnnotation",
285             "class TestClass {}"),
286         CompilerTests.kotlinSource(
287             "test.TestClass.kt", //
288             "package test",
289             "",
290             "@MissingAnnotation",
291             "class TestClass"),
292         (processingEnv, superficialValidation) -> {
293           XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
294           ValidationException exception =
295               assertThrows(
296                   ValidationException.KnownErrorType.class,
297                   () -> superficialValidation.validateElement(testClassElement));
298           // TODO(b/248552462): Javac and KSP should match once this bug is fixed.
299           boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC;
300           assertThat(exception)
301               .hasMessageThat()
302               .contains(
303                   String.format(
304                       NEW_LINES.join(
305                           "Validation trace:",
306                           "  => element (CLASS): test.TestClass",
307                           "  => annotation type: MissingAnnotation",
308                           "  => type (ERROR annotation type): %s"),
309                       isJavac ? "MissingAnnotation" : "error.NonExistentClass"));
310         });
311   }
312 
313   @Test
handlesRecursiveTypeParams()314   public void handlesRecursiveTypeParams() {
315     runSuccessfulTest(
316         CompilerTests.javaSource(
317             "test.TestClass", //
318             "package test;",
319             "",
320             "class TestClass<T extends Comparable<T>> {}"),
321         CompilerTests.kotlinSource(
322             "test.TestClass.kt", //
323             "package test",
324             "",
325             "class TestClass<T : Comparable<T>>"),
326         (processingEnv, superficialValidation) ->
327             superficialValidation.validateElement(processingEnv.findTypeElement("test.TestClass")));
328   }
329 
330   @Test
handlesRecursiveType()331   public void handlesRecursiveType() {
332     runSuccessfulTest(
333         CompilerTests.javaSource(
334             "test.TestClass",
335             "package test;",
336             "",
337             "abstract class TestClass {",
338             "  abstract TestClass foo(TestClass x);",
339             "}"),
340         CompilerTests.kotlinSource(
341             "test.TestClass.kt",
342             "package test",
343             "",
344             "abstract class TestClass {",
345             "  abstract fun foo(x: TestClass): TestClass",
346             "}"),
347         (processingEnv, superficialValidation) ->
348             superficialValidation.validateElement(processingEnv.findTypeElement("test.TestClass")));
349   }
350 
351   @Test
missingWildcardBound()352   public void missingWildcardBound() {
353     runTest(
354         CompilerTests.javaSource(
355             "test.TestClass",
356             "package test;",
357             "",
358             "import java.util.Set;",
359             "",
360             "class TestClass {",
361             "  static final class Foo<T> {}",
362             "",
363             "  Foo<? extends MissingType> extendsTest() {",
364             "    return null;",
365             "  }",
366             "",
367             "  Foo<? super MissingType> superTest() {",
368             "    return null;",
369             "  }",
370             "}"),
371         CompilerTests.kotlinSource(
372             "test.TestClass.kt",
373             "package test",
374             "",
375             "class TestClass {",
376             "  class Foo<T>",
377             "",
378             "  fun extendsTest(): Foo<out MissingType> = TODO()",
379             "",
380             "  fun superTest(): Foo<in MissingType> = TODO()",
381             "}"),
382         (processingEnv, superficialValidation) -> {
383           XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
384           ValidationException exception =
385               assertThrows(
386                   ValidationException.KnownErrorType.class,
387                   () -> superficialValidation.validateElement(testClassElement));
388           // TODO(b/248552462): Javac and KSP should match once this bug is fixed.
389           boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC;
390           assertThat(exception)
391               .hasMessageThat()
392               .contains(
393                   String.format(
394                       NEW_LINES.join(
395                           "Validation trace:",
396                           "  => element (CLASS): test.TestClass",
397                           "  => element (METHOD): extendsTest()",
398                           "  => type (DECLARED return type): test.TestClass.Foo<? extends %1$s>",
399                           "  => type (WILDCARD type argument): ? extends %1$s",
400                           "  => type (ERROR extends bound type): %1$s"),
401                       isJavac ? "MissingType" : "error.NonExistentClass"));
402         });
403   }
404 
405   @Test
missingIntersection()406   public void missingIntersection() {
407     runTest(
408         CompilerTests.javaSource(
409             "test.TestClass",
410             "package test;",
411             "",
412             "class TestClass<T extends Number & Missing> {}"),
413         CompilerTests.kotlinSource(
414             "test.TestClass.kt",
415             "package test",
416             "",
417             "class TestClass<T> where T: Number, T: Missing"),
418         (processingEnv, superficialValidation) -> {
419           if (isKAPT(processingEnv)) {
420             // The KAPT java stub doesn't reference the MissingType symbol (b/268536260#comment2).
421             return;
422           }
423           XTypeElement testClassElement = processingEnv.findTypeElement("test.TestClass");
424           ValidationException exception =
425               assertThrows(
426                   ValidationException.KnownErrorType.class,
427                   () -> superficialValidation.validateElement(testClassElement));
428           // TODO(b/248552462): Javac and KSP should match once this bug is fixed.
429           boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC;
430           assertThat(exception)
431               .hasMessageThat()
432               .contains(
433                   String.format(
434                       NEW_LINES.join(
435                           "Validation trace:",
436                           "  => element (CLASS): test.TestClass",
437                           "  => element (TYPE_PARAMETER): T",
438                           "  => type (ERROR bound type): %s"),
439                       isJavac ? "Missing" : "error.NonExistentClass"));
440         });
441   }
442 
443   @Test
invalidAnnotationValue()444   public void invalidAnnotationValue() {
445     runTest(
446         CompilerTests.javaSource(
447             "test.Outer",
448             "package test;",
449             "",
450             "final class Outer {",
451             "  @interface TestAnnotation {",
452             "    Class[] classes();",
453             "  }",
454             "",
455             "  @TestAnnotation(classes = MissingType.class)",
456             "  static class TestClass {}",
457             "}"),
458         CompilerTests.kotlinSource(
459             "test.Outer.kt",
460             "package test",
461             "",
462             "class Outer {",
463             "  annotation class TestAnnotation(",
464             "    val classes: Array<kotlin.reflect.KClass<*>>",
465             "  )",
466             "",
467             "  @TestAnnotation(classes = [MissingType::class])",
468             "  class TestClass {}",
469             "}"),
470         (processingEnv, superficialValidation) -> {
471           XTypeElement testClassElement = processingEnv.findTypeElement("test.Outer.TestClass");
472           ValidationException exception =
473               assertThrows(
474                   ValidationException.KnownErrorType.class,
475                   () -> superficialValidation.validateElement(testClassElement));
476           // TODO(b/248552462): Javac and KSP should match once this bug is fixed.
477           boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC;
478           String expectedMessage =
479               String.format(
480                   NEW_LINES.join(
481                       "Validation trace:",
482                       "  => element (CLASS): test.Outer.TestClass",
483                       "  => annotation type: test.Outer.TestAnnotation",
484                       "  => annotation: @test.Outer.TestAnnotation(classes={<%1$s>})",
485                       "  => annotation value (TYPE_ARRAY): classes={<%1$s>}",
486                       "  => annotation value (TYPE): classes=<%1$s>"),
487                   isJavac ? "error" : "ERROR TYPE: MissingType");
488           if (!isJavac) {
489             expectedMessage =
490                 NEW_LINES.join(
491                     expectedMessage,
492                     "  => type (ERROR annotation value type): error.NonExistentClass");
493           }
494           assertThat(exception).hasMessageThat().contains(expectedMessage);
495         });
496   }
497 
498   @Test
invalidAnnotationValueOnParameter()499   public void invalidAnnotationValueOnParameter() {
500     runTest(
501         CompilerTests.javaSource(
502             "test.Outer",
503             "package test;",
504             "",
505             "final class Outer {",
506             "  @interface TestAnnotation {",
507             "    Class[] classes();",
508             "  }",
509             "",
510             "  static class TestClass {",
511             "    TestClass(@TestAnnotation(classes = MissingType.class) String strParam) {}",
512             "  }",
513             "}"),
514         CompilerTests.kotlinSource(
515             "test.Outer.kt",
516             "package test",
517             "",
518             "class Outer {",
519             "  annotation class TestAnnotation(",
520             "    val classes: Array<kotlin.reflect.KClass<*>>",
521             "  )",
522             "",
523             "  class TestClass(",
524             "      @TestAnnotation(classes = [MissingType::class]) strParam: String",
525             "  )",
526             "}"),
527         (processingEnv, superficialValidation) -> {
528           if (isKAPT(processingEnv)) {
529             // The KAPT java stub doesn't reference the MissingType symbol (b/268536260#comment2).
530             return;
531           }
532           XTypeElement testClassElement = processingEnv.findTypeElement("test.Outer.TestClass");
533           XConstructorElement constructor = testClassElement.getConstructors().get(0);
534           XVariableElement parameter = constructor.getParameters().get(0);
535           ValidationException exception =
536               assertThrows(
537                   ValidationException.KnownErrorType.class,
538                   () -> superficialValidation.validateElement(parameter));
539           // TODO(b/248552462): Javac and KSP should match once this bug is fixed.
540           boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC;
541           String expectedMessage =
542               String.format(
543                   NEW_LINES.join(
544                       "Validation trace:",
545                       "  => element (CLASS): test.Outer.TestClass",
546                       "  => element (CONSTRUCTOR): TestClass(java.lang.String)",
547                       "  => element (PARAMETER): strParam",
548                       "  => annotation type: test.Outer.TestAnnotation",
549                       "  => annotation: @test.Outer.TestAnnotation(classes={<%1$s>})",
550                       "  => annotation value (TYPE_ARRAY): classes={<%1$s>}",
551                       "  => annotation value (TYPE): classes=<%1$s>"),
552                   isJavac ? "error" : "ERROR TYPE: MissingType");
553           if (!isJavac) {
554             expectedMessage =
555                 NEW_LINES.join(
556                     expectedMessage,
557                     "  => type (ERROR annotation value type): error.NonExistentClass");
558           }
559           assertThat(exception).hasMessageThat().contains(expectedMessage);
560         });
561   }
562 
563   @Test
invalidSuperclassInTypeHierarchy()564   public void invalidSuperclassInTypeHierarchy() {
565     runTest(
566         CompilerTests.javaSource(
567             "test.Outer",
568             "package test;",
569             "",
570             "final class Outer {",
571             "  Child<Long> getChild() { return null; }",
572             "  static class Child<T> extends Parent<T> {}",
573             "  static class Parent<T> extends MissingType<T> {}",
574             "}"),
575         CompilerTests.kotlinSource(
576             "test.Outer.kt",
577             "package test",
578             "",
579             "class Outer {",
580             "  fun getChild(): Child<Long> = TODO()",
581             "  class Child<T> : Parent<T>",
582             "  open class Parent<T> : MissingType<T>",
583             "}"),
584         (processingEnv, superficialValidation) -> {
585           XTypeElement outerElement = processingEnv.findTypeElement("test.Outer");
586           XMethodElement getChildMethod = outerElement.getDeclaredMethods().get(0);
587           ValidationException exception =
588               assertThrows(
589                   ValidationException.KnownErrorType.class,
590                   () ->
591                       superficialValidation.validateTypeHierarchyOf(
592                           "return type", getChildMethod, getChildMethod.getReturnType()));
593           // TODO(b/248552462): Javac and KSP should match once this bug is fixed.
594           boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC;
595           assertThat(exception)
596               .hasMessageThat()
597               .contains(
598                   String.format(
599                       NEW_LINES.join(
600                           "Validation trace:",
601                           "  => element (CLASS): test.Outer",
602                           "  => element (METHOD): getChild()",
603                           "  => type (DECLARED return type): test.Outer.Child<java.lang.Long>",
604                           "  => type (DECLARED supertype): test.Outer.Parent<java.lang.Long>",
605                           "  => type (ERROR supertype): %s"),
606                       isJavac ? "MissingType<T>" : "error.NonExistentClass"));
607         });
608   }
609 
610   @Test
invalidSuperclassTypeParameterInTypeHierarchy()611   public void invalidSuperclassTypeParameterInTypeHierarchy() {
612     runTest(
613         CompilerTests.javaSource(
614             "test.Outer",
615             "package test;",
616             "",
617             "final class Outer {",
618             "  Child getChild() { return null; }",
619             "  static class Child extends Parent<MissingType> {}",
620             "  static class Parent<T> {}",
621             "}"),
622         CompilerTests.kotlinSource(
623             "test.Outer.kt",
624             "package test",
625             "",
626             "class Outer {",
627             "  fun getChild(): Child = TODO()",
628             "  class Child : Parent<MissingType>()",
629             "  open class Parent<T>",
630             "}"),
631         (processingEnv, superficialValidation) -> {
632           XTypeElement outerElement = processingEnv.findTypeElement("test.Outer");
633           XMethodElement getChildMethod = outerElement.getDeclaredMethods().get(0);
634           if (isKAPT(processingEnv)) {
635             // https://youtrack.jetbrains.com/issue/KT-34193/Kapt-CorrectErrorTypes-doesnt-work-for-generics
636             // There's no way to work around this bug in KAPT so validation doesn't catch this case.
637             superficialValidation.validateTypeHierarchyOf(
638                 "return type", getChildMethod, getChildMethod.getReturnType());
639             return;
640           }
641           ValidationException exception =
642               assertThrows(
643                   ValidationException.KnownErrorType.class,
644                   () ->
645                       superficialValidation.validateTypeHierarchyOf(
646                           "return type", getChildMethod, getChildMethod.getReturnType()));
647           // TODO(b/248552462): Javac and KSP should match once this bug is fixed.
648           boolean isJavac = processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC;
649           assertThat(exception)
650               .hasMessageThat()
651               .contains(
652                   String.format(
653                       NEW_LINES.join(
654                           "Validation trace:",
655                           "  => element (CLASS): test.Outer",
656                           "  => element (METHOD): getChild()",
657                           "  => type (DECLARED return type): test.Outer.Child",
658                           "  => type (DECLARED supertype): test.Outer.Parent<%1$s>",
659                           "  => type (ERROR type argument): %1$s"),
660                       isJavac ? "MissingType" : "error.NonExistentClass"));
661         });
662   }
663 
runTest( Source.JavaSource javaSource, Source.KotlinSource kotlinSource, AssertionHandler assertionHandler)664   private void runTest(
665       Source.JavaSource javaSource,
666       Source.KotlinSource kotlinSource,
667       AssertionHandler assertionHandler) {
668     CompilerTests.daggerCompiler(sourceKind == SourceKind.JAVA ? javaSource : kotlinSource)
669         .withProcessingSteps(() -> new AssertingStep(assertionHandler))
670         // We're expecting compiler errors that we assert on in the assertionHandler.
671         .compile(subject -> subject.hasError());
672   }
673 
runSuccessfulTest( Source.JavaSource javaSource, Source.KotlinSource kotlinSource, AssertionHandler assertionHandler)674   private void runSuccessfulTest(
675       Source.JavaSource javaSource,
676       Source.KotlinSource kotlinSource,
677       AssertionHandler assertionHandler) {
678     CompilerTests.daggerCompiler(sourceKind == SourceKind.JAVA ? javaSource : kotlinSource)
679         .withProcessingSteps(() -> new AssertingStep(assertionHandler))
680         .compile(subject -> subject.hasErrorCount(0));
681   }
682 
isKAPT(XProcessingEnv processingEnv)683   private boolean isKAPT(XProcessingEnv processingEnv) {
684     return processingEnv.getBackend() == XProcessingEnv.Backend.JAVAC
685         && sourceKind == SourceKind.KOTLIN;
686   }
687 
688   private interface AssertionHandler {
runAssertions( XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation)689     void runAssertions(
690         XProcessingEnv processingEnv, DaggerSuperficialValidation superficialValidation);
691   }
692 
693   private static final class AssertingStep implements XProcessingStep {
694     private final AssertionHandler assertionHandler;
695     private boolean processed = false;
696 
AssertingStep(AssertionHandler assertionHandler)697     AssertingStep(AssertionHandler assertionHandler) {
698       this.assertionHandler = assertionHandler;
699     }
700 
701     @Override
annotations()702     public final ImmutableSet<String> annotations() {
703       return ImmutableSet.of("*");
704     }
705 
706     @Override
process( XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation)707     public ImmutableSet<XElement> process(
708         XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation) {
709       if (!processed) {
710         processed = true; // only process once.
711         TestComponent component =
712             DaggerDaggerSuperficialValidationTest_TestComponent.factory().create(env);
713         assertionHandler.runAssertions(env, component.superficialValidation());
714       }
715       return ImmutableSet.of();
716     }
717 
718     @Override
processOver( XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation)719     public void processOver(
720         XProcessingEnv env, Map<String, ? extends Set<? extends XElement>> elementsByAnnotation) {}
721   }
722 
723   @Singleton
724   @Component(modules = ProcessingEnvironmentModule.class)
725   interface TestComponent {
superficialValidation()726     DaggerSuperficialValidation superficialValidation();
727 
728     @Component.Factory
729     interface Factory {
create(@indsInstance XProcessingEnv processingEnv)730       TestComponent create(@BindsInstance XProcessingEnv processingEnv);
731     }
732   }
733 }
734