• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.testing.compile.CompilationSubject.assertThat;
20 import static com.google.testing.compile.Compiler.javac;
21 import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
22 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
23 import static dagger.internal.codegen.Compilers.compilerWithOptions;
24 import static dagger.internal.codegen.Compilers.daggerCompiler;
25 import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
26 import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
27 
28 import com.google.auto.common.MoreElements;
29 import com.google.common.base.Predicate;
30 import com.google.common.base.Predicates;
31 import com.google.common.collect.Sets;
32 import com.google.testing.compile.Compilation;
33 import com.google.testing.compile.JavaFileObjects;
34 import dagger.MembersInjector;
35 import java.lang.annotation.Annotation;
36 import java.util.Collection;
37 import java.util.Set;
38 import javax.annotation.processing.AbstractProcessor;
39 import javax.annotation.processing.ProcessingEnvironment;
40 import javax.annotation.processing.RoundEnvironment;
41 import javax.inject.Inject;
42 import javax.lang.model.SourceVersion;
43 import javax.lang.model.element.Element;
44 import javax.lang.model.element.TypeElement;
45 import javax.tools.JavaFileObject;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 import org.junit.runners.Parameterized;
49 import org.junit.runners.Parameterized.Parameters;
50 
51 @RunWith(Parameterized.class)
52 public class ComponentProcessorTest {
53   @Parameters(name = "{0}")
parameters()54   public static Collection<Object[]> parameters() {
55     return CompilerMode.TEST_PARAMETERS;
56   }
57 
58   private final CompilerMode compilerMode;
59 
ComponentProcessorTest(CompilerMode compilerMode)60   public ComponentProcessorTest(CompilerMode compilerMode) {
61     this.compilerMode = compilerMode;
62   }
63 
doubleBindingFromResolvedModules()64   @Test public void doubleBindingFromResolvedModules() {
65     JavaFileObject parent = JavaFileObjects.forSourceLines("test.ParentModule",
66         "package test;",
67         "",
68         "import dagger.Module;",
69         "import dagger.Provides;",
70         "import java.util.List;",
71         "",
72         "@Module",
73         "abstract class ParentModule<A> {",
74         "  @Provides List<A> provideListB(A a) { return null; }",
75         "}");
76     JavaFileObject child = JavaFileObjects.forSourceLines("test.ChildModule",
77         "package test;",
78         "",
79         "import dagger.Module;",
80         "import dagger.Provides;",
81         "",
82         "@Module",
83         "class ChildNumberModule extends ParentModule<Integer> {",
84         "  @Provides Integer provideInteger() { return null; }",
85         "}");
86     JavaFileObject another = JavaFileObjects.forSourceLines("test.AnotherModule",
87         "package test;",
88         "",
89         "import dagger.Module;",
90         "import dagger.Provides;",
91         "import java.util.List;",
92         "",
93         "@Module",
94         "class AnotherModule {",
95         "  @Provides List<Integer> provideListOfInteger() { return null; }",
96         "}");
97     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.BadComponent",
98         "package test;",
99         "",
100         "import dagger.Component;",
101         "import java.util.List;",
102         "",
103         "@Component(modules = {ChildNumberModule.class, AnotherModule.class})",
104         "interface BadComponent {",
105         "  List<Integer> listOfInteger();",
106         "}");
107 
108     Compilation compilation =
109         compilerWithOptions(compilerMode.javacopts())
110             .compile(parent, child, another, componentFile);
111     assertThat(compilation).failed();
112     assertThat(compilation)
113         .hadErrorContaining("List<Integer> is bound multiple times");
114     assertThat(compilation)
115         .hadErrorContaining("@Provides List<Integer> ChildNumberModule.provideListB(Integer)");
116     assertThat(compilation)
117         .hadErrorContaining("@Provides List<Integer> AnotherModule.provideListOfInteger()");
118   }
119 
privateNestedClassWithWarningThatIsAnErrorInComponent()120   @Test public void privateNestedClassWithWarningThatIsAnErrorInComponent() {
121     JavaFileObject outerClass = JavaFileObjects.forSourceLines("test.OuterClass",
122         "package test;",
123         "",
124         "import javax.inject.Inject;",
125         "",
126         "final class OuterClass {",
127         "  @Inject OuterClass(InnerClass innerClass) {}",
128         "",
129         "  private static final class InnerClass {",
130         "    @Inject InnerClass() {}",
131         "  }",
132         "}");
133     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.BadComponent",
134         "package test;",
135         "",
136         "import dagger.Component;",
137         "",
138         "@Component",
139         "interface BadComponent {",
140         "  OuterClass outerClass();",
141         "}");
142     Compilation compilation =
143         compilerWithOptions(
144                 compilerMode.javacopts().append("-Adagger.privateMemberValidation=WARNING"))
145             .compile(outerClass, componentFile);
146     assertThat(compilation).failed();
147     assertThat(compilation)
148         .hadErrorContaining("Dagger does not support injection into private classes");
149   }
150 
simpleComponent()151   @Test public void simpleComponent() {
152     JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
153         "package test;",
154         "",
155         "import javax.inject.Inject;",
156         "",
157         "final class SomeInjectableType {",
158         "  @Inject SomeInjectableType() {}",
159         "}");
160     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
161         "package test;",
162         "",
163         "import dagger.Component;",
164         "import dagger.Lazy;",
165         "import javax.inject.Provider;",
166         "",
167         "@Component",
168         "interface SimpleComponent {",
169         "  SomeInjectableType someInjectableType();",
170         "  Lazy<SomeInjectableType> lazySomeInjectableType();",
171         "  Provider<SomeInjectableType> someInjectableTypeProvider();",
172         "}");
173 
174     JavaFileObject generatedComponent =
175         compilerMode
176             .javaFileBuilder("test.DaggerSimpleComponent")
177             .addLines(
178                 "package test;",
179                 "",
180                 "import dagger.Lazy;",
181                 "import dagger.internal.DoubleCheck;",
182                 IMPORT_GENERATED_ANNOTATION,
183                 "import javax.inject.Provider;",
184                 "",
185                 GENERATED_CODE_ANNOTATIONS,
186                 "final class DaggerSimpleComponent implements SimpleComponent {")
187             .addLinesIn(
188                 FAST_INIT_MODE,
189                 "  private volatile Provider<SomeInjectableType> someInjectableTypeProvider;")
190             .addLines(
191                 "  private DaggerSimpleComponent() {}",
192                 "",
193                 "  public static Builder builder() {",
194                 "    return new Builder();",
195                 "  }",
196                 "",
197                 "  public static SimpleComponent create() {",
198                 "    return new Builder().build();",
199                 "  }",
200                 "",
201                 "  @Override",
202                 "  public SomeInjectableType someInjectableType() {",
203                 "    return new SomeInjectableType();",
204                 "  }",
205                 "",
206                 "  @Override",
207                 "  public Lazy<SomeInjectableType> lazySomeInjectableType() {")
208             .addLinesIn(
209                 DEFAULT_MODE, //
210                 "    return DoubleCheck.lazy(SomeInjectableType_Factory.create());")
211             .addLinesIn(
212                 FAST_INIT_MODE,
213                 "    return DoubleCheck.lazy(someInjectableTypeProvider());")
214             .addLines(
215                 "  }",
216                 "",
217                 "  @Override",
218                 "  public Provider<SomeInjectableType> someInjectableTypeProvider() {")
219             .addLinesIn(
220                 DEFAULT_MODE, //
221                 "    return SomeInjectableType_Factory.create();")
222             .addLinesIn(
223                 FAST_INIT_MODE, //
224                 "    Object local = someInjectableTypeProvider;",
225                 "    if (local == null) {",
226                 "      local = new SwitchingProvider<>(0);",
227                 "      someInjectableTypeProvider = (Provider<SomeInjectableType>) local;",
228                 "    }",
229                 "    return (Provider<SomeInjectableType>) local;")
230             .addLines(
231                 "  }",
232                 "",
233                 "  static final class Builder {",
234                 "    private Builder() {}",
235                 "",
236                 "    public SimpleComponent build() {",
237                 "      return new DaggerSimpleComponent();",
238                 "    }",
239                 "  }")
240             .addLinesIn(
241                 FAST_INIT_MODE,
242                 "  private final class SwitchingProvider<T> implements Provider<T> {",
243                 "    private final int id;",
244                 "",
245                 "    SwitchingProvider(int id) {",
246                 "      this.id = id;",
247                 "    }",
248                 "",
249                 "    @SuppressWarnings(\"unchecked\")",
250                 "    @Override",
251                 "    public T get() {",
252                 "      switch (id) {",
253                 "        case 0: return (T) new SomeInjectableType();",
254                 "        default: throw new AssertionError(id);",
255                 "      }",
256                 "    }",
257                 "  }")
258             .build();
259 
260     Compilation compilation =
261         compilerWithOptions(compilerMode.javacopts())
262             .compile(injectableTypeFile, componentFile);
263     assertThat(compilation).succeeded();
264     assertThat(compilation)
265         .generatedSourceFile("test.DaggerSimpleComponent")
266         .hasSourceEquivalentTo(generatedComponent);
267   }
268 
componentWithScope()269   @Test public void componentWithScope() {
270     JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
271         "package test;",
272         "",
273         "import javax.inject.Inject;",
274         "import javax.inject.Singleton;",
275         "",
276         "@Singleton",
277         "final class SomeInjectableType {",
278         "  @Inject SomeInjectableType() {}",
279         "}");
280     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
281         "package test;",
282         "",
283         "import dagger.Component;",
284         "import dagger.Lazy;",
285         "import javax.inject.Provider;",
286         "import javax.inject.Singleton;",
287         "",
288         "@Singleton",
289         "@Component",
290         "interface SimpleComponent {",
291         "  SomeInjectableType someInjectableType();",
292         "  Lazy<SomeInjectableType> lazySomeInjectableType();",
293         "  Provider<SomeInjectableType> someInjectableTypeProvider();",
294         "}");
295     JavaFileObject generatedComponent =
296         compilerMode
297             .javaFileBuilder("test.DaggerSimpleComponent")
298             .addLines(
299                 "package test;",
300                 "",
301                 GENERATED_CODE_ANNOTATIONS,
302                 "final class DaggerSimpleComponent implements SimpleComponent {")
303             .addLinesIn(
304                 FAST_INIT_MODE,
305                 "  private volatile Object someInjectableType = new MemoizedSentinel();",
306                 "  private volatile Provider<SomeInjectableType> someInjectableTypeProvider;")
307             .addLinesIn(
308                 DEFAULT_MODE,
309                 "  private Provider<SomeInjectableType> someInjectableTypeProvider;",
310                 "",
311                 "  @SuppressWarnings(\"unchecked\")",
312                 "  private void initialize() {",
313                 "    this.someInjectableTypeProvider =",
314                 "        DoubleCheck.provider(SomeInjectableType_Factory.create());",
315                 "  }",
316                 "")
317             .addLines(
318                 "  @Override", //
319                 "  public SomeInjectableType someInjectableType() {")
320             .addLinesIn(
321                 FAST_INIT_MODE,
322                 "    Object local = someInjectableType;",
323                 "    if (local instanceof MemoizedSentinel) {",
324                 "      synchronized (local) {",
325                 "        local = someInjectableType;",
326                 "        if (local instanceof MemoizedSentinel) {",
327                 "          local = new SomeInjectableType();",
328                 "          someInjectableType =",
329                 "              DoubleCheck.reentrantCheck(someInjectableType, local);",
330                 "        }",
331                 "      }",
332                 "    }",
333                 "    return (SomeInjectableType) local;")
334             .addLinesIn(
335                 DEFAULT_MODE, //
336                 "    return someInjectableTypeProvider.get();")
337             .addLines(
338                 "  }",
339                 "",
340                 "  @Override",
341                 "  public Lazy<SomeInjectableType> lazySomeInjectableType() {")
342             .addLinesIn(
343                 DEFAULT_MODE, //
344                 "    return DoubleCheck.lazy(someInjectableTypeProvider);")
345             .addLinesIn(
346                 FAST_INIT_MODE,
347                 "    return DoubleCheck.lazy(someInjectableTypeProvider());")
348             .addLines(
349                 "  }",
350                 "",
351                 "  @Override",
352                 "  public Provider<SomeInjectableType> someInjectableTypeProvider() {")
353             .addLinesIn(
354                 FAST_INIT_MODE, //
355                 "    Object local = someInjectableTypeProvider;",
356                 "    if (local == null) {",
357                 "      local = new SwitchingProvider<>(0);",
358                 "      someInjectableTypeProvider = (Provider<SomeInjectableType>) local;",
359                 "    }",
360                 "    return (Provider<SomeInjectableType>) local;")
361             .addLinesIn(
362                 DEFAULT_MODE, //
363                 "    return someInjectableTypeProvider;")
364             .addLines( //
365                 "  }")
366             .addLinesIn(
367                 FAST_INIT_MODE,
368                 "  private final class SwitchingProvider<T> implements Provider<T> {",
369                 "    private final int id;",
370                 "",
371                 "    SwitchingProvider(int id) {",
372                 "      this.id = id;",
373                 "    }",
374                 "",
375                 "    @SuppressWarnings(\"unchecked\")",
376                 "    @Override",
377                 "    public T get() {",
378                 "      switch (id) {",
379                 "        case 0: return (T) DaggerSimpleComponent.this.someInjectableType();",
380                 "        default: throw new AssertionError(id);",
381                 "      }",
382                 "    }",
383                 "  }")
384             .build();
385     Compilation compilation =
386         compilerWithOptions(compilerMode.javacopts())
387             .compile(injectableTypeFile, componentFile);
388     assertThat(compilation).succeeded();
389     assertThat(compilation)
390         .generatedSourceFile("test.DaggerSimpleComponent")
391         .containsElementsIn(generatedComponent);
392   }
393 
simpleComponentWithNesting()394   @Test public void simpleComponentWithNesting() {
395     JavaFileObject nestedTypesFile = JavaFileObjects.forSourceLines("test.OuterType",
396         "package test;",
397         "",
398         "import dagger.Component;",
399         "import javax.inject.Inject;",
400         "",
401         "final class OuterType {",
402         "  static class A {",
403         "    @Inject A() {}",
404         "  }",
405         "  static class B {",
406         "    @Inject A a;",
407         "  }",
408         "  @Component interface SimpleComponent {",
409         "    A a();",
410         "    void inject(B b);",
411         "  }",
412         "}");
413 
414     JavaFileObject generatedComponent =
415         compilerMode
416             .javaFileBuilder("test.DaggerOuterType_SimpleComponent")
417             .addLines(
418                 "package test;",
419                 "",
420                 GENERATED_CODE_ANNOTATIONS,
421                 "final class DaggerOuterType_SimpleComponent",
422                 "    implements OuterType.SimpleComponent {",
423                 "  private DaggerOuterType_SimpleComponent() {}",
424                 "",
425                 "  @Override",
426                 "  public OuterType.A a() {",
427                 "    return new OuterType.A();",
428                 "  }",
429                 "",
430                 "  @Override",
431                 "  public void inject(OuterType.B b) {",
432                 "    injectB(b);",
433                 "  }",
434                 "",
435                 "  @CanIgnoreReturnValue",
436                 "  private OuterType.B injectB(OuterType.B instance) {",
437                 "    OuterType_B_MembersInjector.injectA(instance, new OuterType.A());",
438                 "    return instance;",
439                 "  }",
440                 "}")
441             .build();
442 
443     Compilation compilation =
444         compilerWithOptions(compilerMode.javacopts()).compile(nestedTypesFile);
445     assertThat(compilation).succeeded();
446     assertThat(compilation)
447         .generatedSourceFile("test.DaggerOuterType_SimpleComponent")
448         .containsElementsIn(generatedComponent);
449   }
450 
componentWithModule()451   @Test public void componentWithModule() {
452     JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
453         "package test;",
454         "",
455         "import javax.inject.Inject;",
456         "",
457         "final class A {",
458         "  @Inject A(B b) {}",
459         "}");
460     JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
461         "package test;",
462         "",
463         "interface B {}");
464     JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C",
465         "package test;",
466         "",
467         "import javax.inject.Inject;",
468         "",
469         "final class C {",
470         "  @Inject C() {}",
471         "}");
472 
473     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
474         "package test;",
475         "",
476         "import dagger.Module;",
477         "import dagger.Provides;",
478         "",
479         "@Module",
480         "final class TestModule {",
481         "  @Provides B b(C c) { return null; }",
482         "}");
483 
484     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
485         "package test;",
486         "",
487         "import dagger.Component;",
488         "import javax.inject.Provider;",
489         "",
490         "@Component(modules = TestModule.class)",
491         "interface TestComponent {",
492         "  A a();",
493         "}");
494 
495     JavaFileObject generatedComponent =
496         compilerMode
497             .javaFileBuilder("test.DaggerTestComponent")
498             .addLines(
499                 "package test;",
500                 "",
501                 "import dagger.internal.Preconditions;",
502                 IMPORT_GENERATED_ANNOTATION,
503                 "",
504                 GENERATED_CODE_ANNOTATIONS,
505                 "final class DaggerTestComponent implements TestComponent {",
506                 "  private final TestModule testModule;",
507                 "",
508                 "  private DaggerTestComponent(TestModule testModuleParam) {",
509                 "    this.testModule = testModuleParam;",
510                 "  }",
511                 "",
512                 "  private B b() {",
513                 "    return TestModule_BFactory.b(testModule, new C());",
514                 "  }",
515                 "",
516                 "  @Override",
517                 "  public A a() {",
518                 "    return new A(b());",
519                 "  }",
520                 "",
521                 "  static final class Builder {",
522                 "    private TestModule testModule;",
523                 "",
524                 "    public Builder testModule(TestModule testModule) {",
525                 "      this.testModule = Preconditions.checkNotNull(testModule);",
526                 "      return this;",
527                 "    }",
528                 "",
529                 "    public TestComponent build() {",
530                 "      if (testModule == null) {",
531                 "        this.testModule = new TestModule();",
532                 "      }",
533                 "      return new DaggerTestComponent(testModule);",
534                 "    }",
535                 "  }",
536                 "}")
537             .build();
538 
539     Compilation compilation =
540         compilerWithOptions(compilerMode.javacopts())
541             .compile(aFile, bFile, cFile, moduleFile, componentFile);
542     assertThat(compilation).succeeded();
543     assertThat(compilation)
544         .generatedSourceFile("test.DaggerTestComponent")
545         .containsElementsIn(generatedComponent);
546   }
547 
548   @Test
componentWithAbstractModule()549   public void componentWithAbstractModule() {
550     JavaFileObject aFile =
551         JavaFileObjects.forSourceLines(
552             "test.A",
553             "package test;",
554             "",
555             "import javax.inject.Inject;",
556             "",
557             "final class A {",
558             "  @Inject A(B b) {}",
559             "}");
560     JavaFileObject bFile =
561         JavaFileObjects.forSourceLines("test.B",
562             "package test;",
563             "",
564             "interface B {}");
565     JavaFileObject cFile =
566         JavaFileObjects.forSourceLines(
567             "test.C",
568             "package test;",
569             "",
570             "import javax.inject.Inject;",
571             "",
572             "final class C {",
573             "  @Inject C() {}",
574             "}");
575 
576     JavaFileObject moduleFile =
577         JavaFileObjects.forSourceLines(
578             "test.TestModule",
579             "package test;",
580             "",
581             "import dagger.Module;",
582             "import dagger.Provides;",
583             "",
584             "@Module",
585             "abstract class TestModule {",
586             "  @Provides static B b(C c) { return null; }",
587             "}");
588 
589     JavaFileObject componentFile =
590         JavaFileObjects.forSourceLines(
591             "test.TestComponent",
592             "package test;",
593             "",
594             "import dagger.Component;",
595             "",
596             "@Component(modules = TestModule.class)",
597             "interface TestComponent {",
598             "  A a();",
599             "}");
600 
601     JavaFileObject generatedComponent =
602         compilerMode
603             .javaFileBuilder("test.DaggerTestComponent")
604             .addLines(
605                 "package test;",
606                 "",
607                 GENERATED_CODE_ANNOTATIONS,
608                 "final class DaggerTestComponent implements TestComponent {",
609                 "  private B b() {",
610                 "    return TestModule_BFactory.b(new C());",
611                 "  }",
612                 "",
613                 "  @Override",
614                 "  public A a() {",
615                 "    return new A(b());",
616                 "  }",
617                 "}")
618             .build();
619 
620     Compilation compilation =
621         compilerWithOptions(compilerMode.javacopts())
622             .compile(aFile, bFile, cFile, moduleFile, componentFile);
623     assertThat(compilation).succeeded();
624     assertThat(compilation)
625         .generatedSourceFile("test.DaggerTestComponent")
626         .containsElementsIn(generatedComponent);
627   }
628 
transitiveModuleDeps()629   @Test public void transitiveModuleDeps() {
630     JavaFileObject always = JavaFileObjects.forSourceLines("test.AlwaysIncluded",
631         "package test;",
632         "",
633         "import dagger.Module;",
634         "",
635         "@Module",
636         "final class AlwaysIncluded {}");
637     JavaFileObject testModule = JavaFileObjects.forSourceLines("test.TestModule",
638         "package test;",
639         "",
640         "import dagger.Module;",
641         "",
642         "@Module(includes = {DepModule.class, AlwaysIncluded.class})",
643         "final class TestModule extends ParentTestModule {}");
644     JavaFileObject parentTest = JavaFileObjects.forSourceLines("test.ParentTestModule",
645         "package test;",
646         "",
647         "import dagger.Module;",
648         "",
649         "@Module(includes = {ParentTestIncluded.class, AlwaysIncluded.class})",
650         "class ParentTestModule {}");
651     JavaFileObject parentTestIncluded = JavaFileObjects.forSourceLines("test.ParentTestIncluded",
652         "package test;",
653         "",
654         "import dagger.Module;",
655         "",
656         "@Module(includes = AlwaysIncluded.class)",
657         "final class ParentTestIncluded {}");
658     JavaFileObject depModule = JavaFileObjects.forSourceLines("test.TestModule",
659         "package test;",
660         "",
661         "import dagger.Module;",
662         "",
663         "@Module(includes = {RefByDep.class, AlwaysIncluded.class})",
664         "final class DepModule extends ParentDepModule {}");
665     JavaFileObject refByDep = JavaFileObjects.forSourceLines("test.RefByDep",
666         "package test;",
667         "",
668         "import dagger.Module;",
669         "",
670         "@Module(includes = AlwaysIncluded.class)",
671         "final class RefByDep extends ParentDepModule {}");
672     JavaFileObject parentDep = JavaFileObjects.forSourceLines("test.ParentDepModule",
673         "package test;",
674         "",
675         "import dagger.Module;",
676         "",
677         "@Module(includes = {ParentDepIncluded.class, AlwaysIncluded.class})",
678         "class ParentDepModule {}");
679     JavaFileObject parentDepIncluded = JavaFileObjects.forSourceLines("test.ParentDepIncluded",
680         "package test;",
681         "",
682         "import dagger.Module;",
683         "",
684         "@Module(includes = AlwaysIncluded.class)",
685         "final class ParentDepIncluded {}");
686 
687     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
688         "package test;",
689         "",
690         "import dagger.Component;",
691         "",
692         "@Component(modules = TestModule.class)",
693         "interface TestComponent {",
694         "}");
695     // Generated code includes all includes, but excludes the parent modules.
696     // The "always" module should only be listed once.
697     JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
698         "test.DaggerTestComponent",
699         "package test;",
700         "",
701         "import dagger.internal.Preconditions;",
702         IMPORT_GENERATED_ANNOTATION,
703         "",
704         GENERATED_CODE_ANNOTATIONS,
705         "final class DaggerTestComponent implements TestComponent {",
706         "  static final class Builder {",
707         "",
708         "    @Deprecated",
709         "    public Builder testModule(TestModule testModule) {",
710         "      Preconditions.checkNotNull(testModule)",
711         "      return this;",
712         "    }",
713         "",
714         "    @Deprecated",
715         "    public Builder parentTestIncluded(ParentTestIncluded parentTestIncluded) {",
716         "      Preconditions.checkNotNull(parentTestIncluded)",
717         "      return this;",
718         "    }",
719         "",
720         "    @Deprecated",
721         "    public Builder alwaysIncluded(AlwaysIncluded alwaysIncluded) {",
722         "      Preconditions.checkNotNull(alwaysIncluded)",
723         "      return this;",
724         "    }",
725         "",
726         "    @Deprecated",
727         "    public Builder depModule(DepModule depModule) {",
728         "      Preconditions.checkNotNull(depModule)",
729         "      return this;",
730         "    }",
731         "",
732         "    @Deprecated",
733         "    public Builder parentDepIncluded(ParentDepIncluded parentDepIncluded) {",
734         "      Preconditions.checkNotNull(parentDepIncluded)",
735         "      return this;",
736         "    }",
737         "",
738         "    @Deprecated",
739         "    public Builder refByDep(RefByDep refByDep) {",
740         "      Preconditions.checkNotNull(refByDep)",
741         "      return this;",
742         "    }",
743         "",
744         "    public TestComponent build() {",
745         "      return new DaggerTestComponent();",
746         "    }",
747         "  }",
748         "}");
749     Compilation compilation =
750         compilerWithOptions(compilerMode.javacopts())
751             .compile(
752                 always,
753                 testModule,
754                 parentTest,
755                 parentTestIncluded,
756                 depModule,
757                 refByDep,
758                 parentDep,
759                 parentDepIncluded,
760                 componentFile);
761     assertThat(compilation).succeeded();
762     assertThat(compilation)
763         .generatedSourceFile("test.DaggerTestComponent")
764         .containsElementsIn(generatedComponent);
765   }
766 
767   @Test
generatedTransitiveModule()768   public void generatedTransitiveModule() {
769     JavaFileObject rootModule = JavaFileObjects.forSourceLines("test.RootModule",
770         "package test;",
771         "",
772         "import dagger.Module;",
773         "",
774         "@Module(includes = GeneratedModule.class)",
775         "final class RootModule {}");
776     JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
777         "package test;",
778         "",
779         "import dagger.Component;",
780         "",
781         "@Component(modules = RootModule.class)",
782         "interface TestComponent {}");
783     assertThat(
784             compilerWithOptions(compilerMode.javacopts()).compile(rootModule, component))
785         .failed();
786     assertThat(
787             daggerCompiler(
788                     new GeneratingProcessor(
789                         "test.GeneratedModule",
790                         "package test;",
791                         "",
792                         "import dagger.Module;",
793                         "",
794                         "@Module",
795                         "final class GeneratedModule {}"))
796                 .compile(rootModule, component))
797         .succeeded();
798   }
799 
800   @Test
generatedModuleInSubcomponent()801   public void generatedModuleInSubcomponent() {
802     JavaFileObject subcomponent =
803         JavaFileObjects.forSourceLines(
804             "test.ChildComponent",
805             "package test;",
806             "",
807             "import dagger.Subcomponent;",
808             "",
809             "@Subcomponent(modules = GeneratedModule.class)",
810             "interface ChildComponent {}");
811     JavaFileObject component =
812         JavaFileObjects.forSourceLines(
813             "test.TestComponent",
814             "package test;",
815             "",
816             "import dagger.Component;",
817             "",
818             "@Component",
819             "interface TestComponent {",
820             "  ChildComponent childComponent();",
821             "}");
822     assertThat(
823             compilerWithOptions(compilerMode.javacopts()).compile(subcomponent, component))
824         .failed();
825     assertThat(
826             daggerCompiler(
827                     new GeneratingProcessor(
828                         "test.GeneratedModule",
829                         "package test;",
830                         "",
831                         "import dagger.Module;",
832                         "",
833                         "@Module",
834                         "final class GeneratedModule {}"))
835                 .compile(subcomponent, component))
836         .succeeded();
837   }
838 
839   @Test
subcomponentNotGeneratedIfNotUsedInGraph()840   public void subcomponentNotGeneratedIfNotUsedInGraph() {
841     JavaFileObject component =
842         JavaFileObjects.forSourceLines(
843             "test.Parent",
844             "package test;",
845             "",
846             "import dagger.Component;",
847             "",
848             "@Component(modules = ParentModule.class)",
849             "interface Parent {",
850             "  String notSubcomponent();",
851             "}");
852     JavaFileObject module =
853         JavaFileObjects.forSourceLines(
854             "test.Parent",
855             "package test;",
856             "",
857             "import dagger.Module;",
858             "import dagger.Provides;",
859             "",
860             "@Module(subcomponents = Child.class)",
861             "class ParentModule {",
862             "  @Provides static String notSubcomponent() { return new String(); }",
863             "}");
864 
865     JavaFileObject subcomponent =
866         JavaFileObjects.forSourceLines(
867             "test.Child",
868             "package test;",
869             "",
870             "import dagger.Subcomponent;",
871             "",
872             "@Subcomponent",
873             "interface Child {",
874             "  @Subcomponent.Builder",
875             "  interface Builder {",
876             "    Child build();",
877             "  }",
878             "}");
879 
880     JavaFileObject generatedComponent =
881         JavaFileObjects.forSourceLines(
882             "test.DaggerParent",
883             "package test;",
884             "",
885             "import dagger.internal.Preconditions;",
886             IMPORT_GENERATED_ANNOTATION,
887             "",
888             GENERATED_CODE_ANNOTATIONS,
889             "final class DaggerParent implements Parent {",
890             "",
891             "  private DaggerParent() {}",
892             "",
893             "  public static Builder builder() {",
894             "    return new Builder();",
895             "  }",
896             "",
897             "  public static Parent create() {",
898             "    return new Builder().build();",
899             "  }",
900             "",
901             "  @Override",
902             "  public String notSubcomponent() {",
903             "    return ParentModule_NotSubcomponentFactory.notSubcomponent();",
904             "  }",
905             "",
906             "  static final class Builder {",
907             "",
908             "    private Builder() {}",
909             "",
910             "    @Deprecated",
911             "    public Builder parentModule(ParentModule parentModule) {",
912             "      Preconditions.checkNotNull(parentModule);",
913             "      return this;",
914             "    }",
915             "",
916             "    public Parent build() {",
917             "      return new DaggerParent();",
918             "    }",
919             "  }",
920             "}");
921     Compilation compilation =
922         compilerWithOptions(compilerMode.javacopts())
923             .compile(component, module, subcomponent);
924     assertThat(compilation).succeeded();
925     assertThat(compilation)
926         .generatedSourceFile("test.DaggerParent")
927         .hasSourceEquivalentTo(generatedComponent);
928   }
929 
930   @Test
testDefaultPackage()931   public void testDefaultPackage() {
932     JavaFileObject aClass = JavaFileObjects.forSourceLines("AClass", "class AClass {}");
933     JavaFileObject bClass = JavaFileObjects.forSourceLines("BClass",
934         "import javax.inject.Inject;",
935         "",
936         "class BClass {",
937         "  @Inject BClass(AClass a) {}",
938         "}");
939     JavaFileObject aModule = JavaFileObjects.forSourceLines("AModule",
940         "import dagger.Module;",
941         "import dagger.Provides;",
942         "",
943         "@Module class AModule {",
944         "  @Provides AClass aClass() {",
945         "    return new AClass();",
946         "  }",
947         "}");
948     JavaFileObject component = JavaFileObjects.forSourceLines("SomeComponent",
949         "import dagger.Component;",
950         "",
951         "@Component(modules = AModule.class)",
952         "interface SomeComponent {",
953         "  BClass bClass();",
954         "}");
955     assertThat(
956             compilerWithOptions(compilerMode.javacopts())
957                 .compile(aModule, aClass, bClass, component))
958         .succeeded();
959   }
960 
membersInjection()961   @Test public void membersInjection() {
962     JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
963         "package test;",
964         "",
965         "import javax.inject.Inject;",
966         "",
967         "final class SomeInjectableType {",
968         "  @Inject SomeInjectableType() {}",
969         "}");
970     JavaFileObject injectedTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectedType",
971         "package test;",
972         "",
973         "import javax.inject.Inject;",
974         "",
975         "final class SomeInjectedType {",
976         "  @Inject SomeInjectableType injectedField;",
977         "  SomeInjectedType() {}",
978         "}");
979     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
980         "package test;",
981         "",
982         "import dagger.Component;",
983         "import dagger.Lazy;",
984         "import javax.inject.Provider;",
985         "",
986         "@Component",
987         "interface SimpleComponent {",
988         "  void inject(SomeInjectedType instance);",
989         "  SomeInjectedType injectAndReturn(SomeInjectedType instance);",
990         "}");
991 
992     JavaFileObject generatedComponent =
993         compilerMode
994             .javaFileBuilder("test.DaggerSimpleComponent")
995             .addLines(
996                 "package test;",
997                 "",
998                 "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
999                 "",
1000                 GENERATED_CODE_ANNOTATIONS,
1001                 "final class DaggerSimpleComponent implements SimpleComponent {",
1002                 "  @Override",
1003                 "  public void inject(SomeInjectedType instance) {",
1004                 "    injectSomeInjectedType(instance);",
1005                 "  }",
1006                 "",
1007                 "  @Override",
1008                 "  public SomeInjectedType injectAndReturn(SomeInjectedType instance) {",
1009                 "    return injectSomeInjectedType(instance);",
1010                 "  }",
1011                 "",
1012                 "  @CanIgnoreReturnValue",
1013                 "  private SomeInjectedType injectSomeInjectedType(SomeInjectedType instance) {",
1014                 "    SomeInjectedType_MembersInjector.injectInjectedField(",
1015                 "        instance, new SomeInjectableType());",
1016                 "    return instance;",
1017                 "  }",
1018                 "}")
1019             .build();
1020 
1021     Compilation compilation =
1022         compilerWithOptions(compilerMode.javacopts())
1023             .compile(injectableTypeFile, injectedTypeFile, componentFile);
1024     assertThat(compilation).succeeded();
1025     assertThat(compilation)
1026         .generatedSourceFile("test.DaggerSimpleComponent")
1027         .containsElementsIn(generatedComponent);
1028   }
1029 
componentInjection()1030   @Test public void componentInjection() {
1031     JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
1032         "package test;",
1033         "",
1034         "import javax.inject.Inject;",
1035         "",
1036         "final class SomeInjectableType {",
1037         "  @Inject SomeInjectableType(SimpleComponent component) {}",
1038         "}");
1039     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
1040         "package test;",
1041         "",
1042         "import dagger.Component;",
1043         "import dagger.Lazy;",
1044         "import javax.inject.Provider;",
1045         "",
1046         "@Component",
1047         "interface SimpleComponent {",
1048         "  SomeInjectableType someInjectableType();",
1049         "  Provider<SimpleComponent> selfProvider();",
1050         "}");
1051     JavaFileObject generatedComponent =
1052         JavaFileObjects.forSourceLines(
1053             "test.DaggerSimpleComponent",
1054             "package test;",
1055             "",
1056             GENERATED_CODE_ANNOTATIONS,
1057             "final class DaggerSimpleComponent implements SimpleComponent {",
1058             "  private Provider<SimpleComponent> simpleComponentProvider;",
1059             "",
1060             "  @SuppressWarnings(\"unchecked\")",
1061             "  private void initialize() {",
1062             "    this.simpleComponentProvider = InstanceFactory.create((SimpleComponent) this);",
1063             "  }",
1064             "",
1065             "  @Override",
1066             "  public SomeInjectableType someInjectableType() {",
1067             "    return new SomeInjectableType(this)",
1068             "  }",
1069             "",
1070             "  @Override",
1071             "  public Provider<SimpleComponent> selfProvider() {",
1072             "    return simpleComponentProvider;",
1073             "  }",
1074             "}");
1075     Compilation compilation =
1076         compilerWithOptions(compilerMode.javacopts())
1077             .compile(injectableTypeFile, componentFile);
1078     assertThat(compilation).succeeded();
1079     assertThat(compilation)
1080         .generatedSourceFile("test.DaggerSimpleComponent")
1081         .containsElementsIn(generatedComponent);
1082   }
1083 
membersInjectionInsideProvision()1084   @Test public void membersInjectionInsideProvision() {
1085     JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
1086         "package test;",
1087         "",
1088         "import javax.inject.Inject;",
1089         "",
1090         "final class SomeInjectableType {",
1091         "  @Inject SomeInjectableType() {}",
1092         "}");
1093     JavaFileObject injectedTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectedType",
1094         "package test;",
1095         "",
1096         "import javax.inject.Inject;",
1097         "",
1098         "final class SomeInjectedType {",
1099         "  @Inject SomeInjectableType injectedField;",
1100         "  @Inject SomeInjectedType() {}",
1101         "}");
1102     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
1103         "package test;",
1104         "",
1105         "import dagger.Component;",
1106         "",
1107         "@Component",
1108         "interface SimpleComponent {",
1109         "  SomeInjectedType createAndInject();",
1110         "}");
1111 
1112     JavaFileObject generatedComponent =
1113         compilerMode
1114             .javaFileBuilder("test.DaggerSimpleComponent")
1115             .addLines(
1116                 "package test;",
1117                 "",
1118                 "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
1119                 "",
1120                 GENERATED_CODE_ANNOTATIONS,
1121                 "final class DaggerSimpleComponent implements SimpleComponent {",
1122                 "  @Override",
1123                 "  public SomeInjectedType createAndInject() {",
1124                 "    return injectSomeInjectedType(",
1125                 "        SomeInjectedType_Factory.newInstance());",
1126                 "  }",
1127                 "",
1128                 "  @CanIgnoreReturnValue",
1129                 "  private SomeInjectedType injectSomeInjectedType(SomeInjectedType instance) {",
1130                 "    SomeInjectedType_MembersInjector.injectInjectedField(",
1131                 "        instance, new SomeInjectableType());",
1132                 "    return instance;",
1133                 "  }",
1134                 "}")
1135             .build();
1136 
1137     Compilation compilation =
1138         compilerWithOptions(compilerMode.javacopts())
1139             .compile(injectableTypeFile, injectedTypeFile, componentFile);
1140     assertThat(compilation).succeeded();
1141     assertThat(compilation)
1142         .generatedSourceFile("test.DaggerSimpleComponent")
1143         .containsElementsIn(generatedComponent);
1144   }
1145 
componentDependency()1146   @Test public void componentDependency() {
1147     JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
1148         "package test;",
1149         "",
1150         "import javax.inject.Inject;",
1151         "",
1152         "final class A {",
1153         "  @Inject A() {}",
1154         "}");
1155     JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
1156         "package test;",
1157         "",
1158         "import javax.inject.Inject;",
1159         "import javax.inject.Provider;",
1160         "",
1161         "final class B {",
1162         "  @Inject B(Provider<A> a) {}",
1163         "}");
1164     JavaFileObject aComponentFile = JavaFileObjects.forSourceLines("test.AComponent",
1165         "package test;",
1166         "",
1167         "import dagger.Component;",
1168         "",
1169         "@Component",
1170         "interface AComponent {",
1171         "  A a();",
1172         "}");
1173     JavaFileObject bComponentFile = JavaFileObjects.forSourceLines("test.AComponent",
1174         "package test;",
1175         "",
1176         "import dagger.Component;",
1177         "",
1178         "@Component(dependencies = AComponent.class)",
1179         "interface BComponent {",
1180         "  B b();",
1181         "}");
1182     JavaFileObject generatedComponent =
1183         compilerMode
1184             .javaFileBuilder("test.DaggerBComponent")
1185             .addLines(
1186                 "package test;",
1187                 "",
1188                 GENERATED_CODE_ANNOTATIONS,
1189                 "final class DaggerBComponent implements BComponent {")
1190             .addLinesIn(DEFAULT_MODE, "  private Provider<A> aProvider;")
1191             .addLinesIn(
1192                 FAST_INIT_MODE,
1193                 "  private final AComponent aComponent;",
1194                 "  private volatile Provider<A> aProvider;",
1195                 "",
1196                 "  private DaggerBComponent(AComponent aComponentParam) {",
1197                 "    this.aComponent = aComponentParam;",
1198                 "  }",
1199                 "",
1200                 "  private Provider<A> aProvider() {",
1201                 "    Object local = aProvider;",
1202                 "    if (local == null) {",
1203                 "      local = new SwitchingProvider<>(0);",
1204                 "      aProvider = (Provider<A>) local;",
1205                 "    }",
1206                 "    return (Provider<A>) local;",
1207                 "  }")
1208             .addLinesIn(
1209                 DEFAULT_MODE,
1210                 "  @SuppressWarnings(\"unchecked\")",
1211                 "  private void initialize(final AComponent aComponentParam) {",
1212                 "    this.aProvider = new test_AComponent_a(aComponentParam);",
1213                 "  }")
1214             .addLines("", "  @Override", "  public B b() {")
1215             .addLinesIn(DEFAULT_MODE, "    return new B(aProvider);")
1216             .addLinesIn(FAST_INIT_MODE, "    return new B(aProvider());")
1217             .addLines(
1218                 "  }",
1219                 "",
1220                 "  static final class Builder {",
1221                 "    private AComponent aComponent;",
1222                 "",
1223                 "    public Builder aComponent(AComponent aComponent) {",
1224                 "      this.aComponent = Preconditions.checkNotNull(aComponent);",
1225                 "      return this;",
1226                 "    }",
1227                 "",
1228                 "    public BComponent build() {",
1229                 "      Preconditions.checkBuilderRequirement(aComponent, AComponent.class);",
1230                 "      return new DaggerBComponent(aComponent);",
1231                 "    }",
1232                 "  }")
1233             .addLinesIn(
1234                 DEFAULT_MODE,
1235                 "  private static class test_AComponent_a implements Provider<A> {",
1236                 "    private final AComponent aComponent;",
1237                 "    ",
1238                 "    test_AComponent_a(AComponent aComponent) {",
1239                 "        this.aComponent = aComponent;",
1240                 "    }",
1241                 "    ",
1242                 "    @Override()",
1243                 "    public A get() {",
1244                 "      return Preconditions.checkNotNullFromComponent(aComponent.a());",
1245                 "    }",
1246                 "  }",
1247                 "}")
1248             .addLinesIn(
1249                 FAST_INIT_MODE,
1250                 "  private final class SwitchingProvider<T> implements Provider<T> {",
1251                 "    @SuppressWarnings(\"unchecked\")",
1252                 "    @Override",
1253                 "    public T get() {",
1254                 "      switch (id) {",
1255                 "        case 0:",
1256                 "          return (T)",
1257                 "              Preconditions.checkNotNullFromComponent(",
1258                 "                  DaggerBComponent.this.aComponent.a());",
1259                 "        default:",
1260                 "          throw new AssertionError(id);",
1261                 "      }",
1262                 "    }",
1263                 "  }")
1264             .build();
1265     Compilation compilation =
1266         compilerWithOptions(compilerMode.javacopts())
1267             .compile(aFile, bFile, aComponentFile, bComponentFile);
1268     assertThat(compilation).succeeded();
1269     assertThat(compilation)
1270         .generatedSourceFile("test.DaggerBComponent")
1271         .containsElementsIn(generatedComponent);
1272   }
1273 
moduleNameCollision()1274   @Test public void moduleNameCollision() {
1275     JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
1276         "package test;",
1277         "",
1278         "public final class A {}");
1279     JavaFileObject otherAFile = JavaFileObjects.forSourceLines("other.test.A",
1280         "package other.test;",
1281         "",
1282         "public final class A {}");
1283 
1284     JavaFileObject moduleFile = JavaFileObjects.forSourceLines("test.TestModule",
1285         "package test;",
1286         "",
1287         "import dagger.Module;",
1288         "import dagger.Provides;",
1289         "",
1290         "@Module",
1291         "public final class TestModule {",
1292         "  @Provides A a() { return null; }",
1293         "}");
1294     JavaFileObject otherModuleFile = JavaFileObjects.forSourceLines("other.test.TestModule",
1295         "package other.test;",
1296         "",
1297         "import dagger.Module;",
1298         "import dagger.Provides;",
1299         "",
1300         "@Module",
1301         "public final class TestModule {",
1302         "  @Provides A a() { return null; }",
1303         "}");
1304 
1305     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
1306         "package test;",
1307         "",
1308         "import dagger.Component;",
1309         "import javax.inject.Provider;",
1310         "",
1311         "@Component(modules = {TestModule.class, other.test.TestModule.class})",
1312         "interface TestComponent {",
1313         "  A a();",
1314         "  other.test.A otherA();",
1315         "}");
1316     JavaFileObject generatedComponent =
1317         JavaFileObjects.forSourceLines(
1318             "test.DaggerTestComponent",
1319             "package test;",
1320             "",
1321             GENERATED_CODE_ANNOTATIONS,
1322             "final class DaggerTestComponent implements TestComponent {",
1323             "  private final TestModule testModule;",
1324             "  private final other.test.TestModule testModule2;",
1325             "",
1326             "  private DaggerTestComponent(",
1327             "      TestModule testModuleParam,",
1328             "      other.test.TestModule testModule2Param) {",
1329             "    this.testModule = testModuleParam;",
1330             "    this.testModule2 = testModule2Param;",
1331             "  }",
1332             "",
1333             "  @Override",
1334             "  public A a() {",
1335             "    return TestModule_AFactory.a(testModule);",
1336             "  }",
1337             "",
1338             "  @Override",
1339             "  public other.test.A otherA() {",
1340             "    return other.test.TestModule_AFactory.a(testModule2);",
1341             "  }",
1342             "",
1343             "  static final class Builder {",
1344             "    private TestModule testModule;",
1345             "    private other.test.TestModule testModule2;",
1346             "",
1347             "    public Builder testModule(TestModule testModule) {",
1348             "      this.testModule = Preconditions.checkNotNull(testModule);",
1349             "      return this;",
1350             "    }",
1351             "",
1352             "    public Builder testModule(other.test.TestModule testModule) {",
1353             "      this.testModule2 = Preconditions.checkNotNull(testModule);",
1354             "      return this;",
1355             "    }",
1356             "",
1357             "    public TestComponent build() {",
1358             "      if (testModule == null) {",
1359             "        this.testModule = new TestModule();",
1360             "      }",
1361             "      if (testModule2 == null) {",
1362             "        this.testModule2 = new other.test.TestModule();",
1363             "      }",
1364             "      return new DaggerTestComponent(testModule, testModule2);",
1365             "    }",
1366             "  }",
1367             "}");
1368     Compilation compilation =
1369         compilerWithOptions(compilerMode.javacopts())
1370             .compile(aFile, otherAFile, moduleFile, otherModuleFile, componentFile);
1371     assertThat(compilation).succeeded();
1372     assertThat(compilation)
1373         .generatedSourceFile("test.DaggerTestComponent")
1374         .containsElementsIn(generatedComponent);
1375   }
1376 
ignoresDependencyMethodsFromObject()1377   @Test public void ignoresDependencyMethodsFromObject() {
1378     JavaFileObject injectedTypeFile =
1379         JavaFileObjects.forSourceLines(
1380             "test.InjectedType",
1381             "package test;",
1382             "",
1383             "import javax.inject.Inject;",
1384             "import javax.inject.Provider;",
1385             "",
1386             "final class InjectedType {",
1387             "  @Inject InjectedType(",
1388             "      String stringInjection,",
1389             "      int intInjection,",
1390             "      AComponent aComponent,",
1391             "      Class<AComponent> aClass) {}",
1392             "}");
1393     JavaFileObject aComponentFile =
1394         JavaFileObjects.forSourceLines(
1395             "test.AComponent",
1396             "package test;",
1397             "",
1398             "class AComponent {",
1399             "  String someStringInjection() {",
1400             "    return \"injectedString\";",
1401             "  }",
1402             "",
1403             "  int someIntInjection() {",
1404             "    return 123;",
1405             "  }",
1406             "",
1407             "  Class<AComponent> someClassInjection() {",
1408             "    return AComponent.class;",
1409             "  }",
1410             "",
1411             "  @Override",
1412             "  public String toString() {",
1413             "    return null;",
1414             "  }",
1415             "",
1416             "  @Override",
1417             "  public int hashCode() {",
1418             "    return 456;",
1419             "  }",
1420             "",
1421             "  @Override",
1422             "  public AComponent clone() {",
1423             "    return null;",
1424             "  }",
1425             "}");
1426     JavaFileObject bComponentFile =
1427         JavaFileObjects.forSourceLines(
1428             "test.AComponent",
1429             "package test;",
1430             "",
1431             "import dagger.Component;",
1432             "",
1433             "@Component(dependencies = AComponent.class)",
1434             "interface BComponent {",
1435             "  InjectedType injectedType();",
1436             "}");
1437 
1438     JavaFileObject generatedComponent =
1439         JavaFileObjects.forSourceLines(
1440             "test.DaggerBComponent",
1441             "package test;",
1442             "",
1443             "import dagger.internal.Preconditions;",
1444             IMPORT_GENERATED_ANNOTATION,
1445             "",
1446             GENERATED_CODE_ANNOTATIONS,
1447             "final class DaggerBComponent implements BComponent {",
1448             "  private final AComponent aComponent;",
1449             "",
1450             "  private DaggerBComponent(AComponent aComponentParam) {",
1451             "    this.aComponent = aComponentParam;",
1452             "  }",
1453             "",
1454             "  @Override",
1455             "  public InjectedType injectedType() {",
1456             "    return new InjectedType(",
1457             "        Preconditions.checkNotNullFromComponent(",
1458             "            aComponent.someStringInjection()),",
1459             "        aComponent.someIntInjection(),",
1460             "        aComponent,",
1461             "        Preconditions.checkNotNullFromComponent(",
1462             "            aComponent.someClassInjection()));",
1463             "  }",
1464             "}");
1465 
1466     Compilation compilation =
1467         compilerWithOptions(compilerMode.javacopts())
1468             .compile(injectedTypeFile, aComponentFile, bComponentFile);
1469     assertThat(compilation).succeeded();
1470     assertThat(compilation)
1471         .generatedSourceFile("test.DaggerBComponent")
1472         .containsElementsIn(generatedComponent);
1473   }
1474 
resolutionOrder()1475   @Test public void resolutionOrder() {
1476     JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
1477         "package test;",
1478         "",
1479         "import javax.inject.Inject;",
1480         "",
1481         "final class A {",
1482         "  @Inject A(B b) {}",
1483         "}");
1484     JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
1485         "package test;",
1486         "",
1487         "import javax.inject.Inject;",
1488         "",
1489         "final class B {",
1490         "  @Inject B(C c) {}",
1491         "}");
1492     JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C",
1493         "package test;",
1494         "",
1495         "import javax.inject.Inject;",
1496         "",
1497         "final class C {",
1498         "  @Inject C() {}",
1499         "}");
1500     JavaFileObject xFile = JavaFileObjects.forSourceLines("test.X",
1501         "package test;",
1502         "",
1503         "import javax.inject.Inject;",
1504         "",
1505         "final class X {",
1506         "  @Inject X(C c) {}",
1507         "}");
1508 
1509     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
1510         "package test;",
1511         "",
1512         "import dagger.Component;",
1513         "import javax.inject.Provider;",
1514         "",
1515         "@Component",
1516         "interface TestComponent {",
1517         "  A a();",
1518         "  C c();",
1519         "  X x();",
1520         "}");
1521 
1522     JavaFileObject generatedComponent =
1523         compilerMode
1524             .javaFileBuilder("test.DaggerTestComponent")
1525             .addLines(
1526                 "package test;",
1527                 "",
1528                 IMPORT_GENERATED_ANNOTATION,
1529                 "",
1530                 GENERATED_CODE_ANNOTATIONS,
1531                 "final class DaggerTestComponent implements TestComponent {",
1532                 "  private B b() {",
1533                 "    return new B(new C());",
1534                 "  }",
1535                 "",
1536                 "  @Override",
1537                 "  public A a() {",
1538                 "    return new A(b());",
1539                 "  }",
1540                 "",
1541                 "  @Override",
1542                 "  public C c() {",
1543                 "    return new C();",
1544                 "  }",
1545                 "",
1546                 "  @Override",
1547                 "  public X x() {",
1548                 "    return new X(new C());",
1549                 "  }",
1550                 "}")
1551             .build();
1552 
1553     Compilation compilation =
1554         compilerWithOptions(compilerMode.javacopts())
1555             .compile(aFile, bFile, cFile, xFile, componentFile);
1556     assertThat(compilation).succeeded();
1557     assertThat(compilation)
1558         .generatedSourceFile("test.DaggerTestComponent")
1559         .containsElementsIn(generatedComponent);
1560   }
1561 
simpleComponent_redundantComponentMethod()1562   @Test public void simpleComponent_redundantComponentMethod() {
1563     JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
1564         "package test;",
1565         "",
1566         "import javax.inject.Inject;",
1567         "",
1568         "final class SomeInjectableType {",
1569         "  @Inject SomeInjectableType() {}",
1570         "}");
1571     JavaFileObject componentSupertypeAFile = JavaFileObjects.forSourceLines("test.SupertypeA",
1572         "package test;",
1573         "",
1574         "import dagger.Component;",
1575         "import dagger.Lazy;",
1576         "import javax.inject.Provider;",
1577         "",
1578         "@Component",
1579         "interface SupertypeA {",
1580         "  SomeInjectableType someInjectableType();",
1581         "}");
1582     JavaFileObject componentSupertypeBFile = JavaFileObjects.forSourceLines("test.SupertypeB",
1583         "package test;",
1584         "",
1585         "import dagger.Component;",
1586         "import dagger.Lazy;",
1587         "import javax.inject.Provider;",
1588         "",
1589         "@Component",
1590         "interface SupertypeB {",
1591         "  SomeInjectableType someInjectableType();",
1592         "}");
1593     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
1594         "package test;",
1595         "",
1596         "import dagger.Component;",
1597         "import dagger.Lazy;",
1598         "import javax.inject.Provider;",
1599         "",
1600         "@Component",
1601         "interface SimpleComponent extends SupertypeA, SupertypeB {",
1602         "}");
1603     JavaFileObject generatedComponent =
1604         JavaFileObjects.forSourceLines(
1605             "test.DaggerSimpleComponent",
1606             "package test;",
1607             "",
1608             IMPORT_GENERATED_ANNOTATION,
1609             "",
1610             GENERATED_CODE_ANNOTATIONS,
1611             "final class DaggerSimpleComponent implements SimpleComponent {",
1612             "  private DaggerSimpleComponent() {}",
1613             "",
1614             "  public static Builder builder() {",
1615             "    return new Builder();",
1616             "  }",
1617             "",
1618             "  public static SimpleComponent create() {",
1619             "    return new Builder().build();",
1620             "  }",
1621             "",
1622             "  @Override",
1623             "  public SomeInjectableType someInjectableType() {",
1624             "    return new SomeInjectableType();",
1625             "  }",
1626             "",
1627             "  static final class Builder {",
1628             "    private Builder() {}",
1629             "",
1630             "    public SimpleComponent build() {",
1631             "      return new DaggerSimpleComponent();",
1632             "    }",
1633             "  }",
1634             "}");
1635     Compilation compilation =
1636         compilerWithOptions(compilerMode.javacopts())
1637             .compile(
1638                 injectableTypeFile,
1639                 componentSupertypeAFile,
1640                 componentSupertypeBFile,
1641                 componentFile);
1642     assertThat(compilation).succeeded();
1643     assertThat(compilation)
1644         .generatedSourceFile("test.DaggerSimpleComponent")
1645         .hasSourceEquivalentTo(generatedComponent);
1646   }
1647 
simpleComponent_inheritedComponentMethodDep()1648   @Test public void simpleComponent_inheritedComponentMethodDep() {
1649     JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
1650         "package test;",
1651         "",
1652         "import javax.inject.Inject;",
1653         "",
1654         "final class SomeInjectableType {",
1655         "  @Inject SomeInjectableType() {}",
1656         "}");
1657     JavaFileObject componentSupertype = JavaFileObjects.forSourceLines("test.Supertype",
1658         "package test;",
1659         "",
1660         "import dagger.Component;",
1661         "",
1662         "@Component",
1663         "interface Supertype {",
1664         "  SomeInjectableType someInjectableType();",
1665         "}");
1666     JavaFileObject depComponentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
1667         "package test;",
1668         "",
1669         "import dagger.Component;",
1670         "",
1671         "@Component",
1672         "interface SimpleComponent extends Supertype {",
1673         "}");
1674     JavaFileObject generatedComponent =
1675         JavaFileObjects.forSourceLines(
1676             "test.DaggerSimpleComponent",
1677             "package test;",
1678             "",
1679             GENERATED_CODE_ANNOTATIONS,
1680             "final class DaggerSimpleComponent implements SimpleComponent {",
1681             "  @Override",
1682             "  public SomeInjectableType someInjectableType() {",
1683             "    return new SomeInjectableType();",
1684             "  }",
1685             "}");
1686     Compilation compilation =
1687         compilerWithOptions(compilerMode.javacopts())
1688             .compile(injectableTypeFile, componentSupertype, depComponentFile);
1689     assertThat(compilation).succeeded();
1690     assertThat(compilation)
1691         .generatedSourceFile("test.DaggerSimpleComponent")
1692         .containsElementsIn(generatedComponent);
1693   }
1694 
wildcardGenericsRequiresAtProvides()1695   @Test public void wildcardGenericsRequiresAtProvides() {
1696     JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
1697         "package test;",
1698         "",
1699         "import javax.inject.Inject;",
1700         "",
1701         "final class A {",
1702         "  @Inject A() {}",
1703         "}");
1704     JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
1705         "package test;",
1706         "",
1707         "import javax.inject.Inject;",
1708         "import javax.inject.Provider;",
1709         "",
1710         "final class B<T> {",
1711         "  @Inject B(T t) {}",
1712         "}");
1713     JavaFileObject cFile = JavaFileObjects.forSourceLines("test.C",
1714         "package test;",
1715         "",
1716         "import javax.inject.Inject;",
1717         "import javax.inject.Provider;",
1718         "",
1719         "final class C {",
1720         "  @Inject C(B<? extends A> bA) {}",
1721         "}");
1722     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
1723         "package test;",
1724         "",
1725         "import dagger.Component;",
1726         "import dagger.Lazy;",
1727         "import javax.inject.Provider;",
1728         "",
1729         "@Component",
1730         "interface SimpleComponent {",
1731         "  C c();",
1732         "}");
1733     Compilation compilation =
1734         compilerWithOptions(compilerMode.javacopts())
1735             .compile(aFile, bFile, cFile, componentFile);
1736     assertThat(compilation).failed();
1737     assertThat(compilation)
1738         .hadErrorContaining(
1739             "test.B<? extends test.A> cannot be provided without an @Provides-annotated method");
1740   }
1741 
1742   // https://github.com/google/dagger/issues/630
1743   @Test
arrayKeyRequiresAtProvides()1744   public void arrayKeyRequiresAtProvides() {
1745     JavaFileObject component =
1746         JavaFileObjects.forSourceLines(
1747             "test.TestComponent",
1748             "package test;",
1749             "",
1750             "import dagger.Component;",
1751             "",
1752             "@Component",
1753             "interface TestComponent {",
1754             "  String[] array();",
1755             "}");
1756     Compilation compilation =
1757         compilerWithOptions(compilerMode.javacopts()).compile(component);
1758     assertThat(compilation).failed();
1759     assertThat(compilation)
1760         .hadErrorContaining("String[] cannot be provided without an @Provides-annotated method");
1761   }
1762 
1763   @Test
componentImplicitlyDependsOnGeneratedType()1764   public void componentImplicitlyDependsOnGeneratedType() {
1765     JavaFileObject injectableTypeFile = JavaFileObjects.forSourceLines("test.SomeInjectableType",
1766         "package test;",
1767         "",
1768         "import javax.inject.Inject;",
1769         "",
1770         "final class SomeInjectableType {",
1771         "  @Inject SomeInjectableType(GeneratedType generatedType) {}",
1772         "}");
1773     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.SimpleComponent",
1774         "package test;",
1775         "",
1776         "import dagger.Component;",
1777         "",
1778         "@Component",
1779         "interface SimpleComponent {",
1780         "  SomeInjectableType someInjectableType();",
1781         "}");
1782     Compilation compilation =
1783         daggerCompiler(
1784                 new GeneratingProcessor(
1785                     "test.GeneratedType",
1786                     "package test;",
1787                     "",
1788                     "import javax.inject.Inject;",
1789                     "",
1790                     "final class GeneratedType {",
1791                     "  @Inject GeneratedType() {}",
1792                     "}"))
1793             .withOptions(compilerMode.javacopts())
1794             .compile(injectableTypeFile, componentFile);
1795     assertThat(compilation).succeeded();
1796     assertThat(compilation).generatedSourceFile("test.DaggerSimpleComponent");
1797   }
1798 
1799   @Test
componentSupertypeDependsOnGeneratedType()1800   public void componentSupertypeDependsOnGeneratedType() {
1801     JavaFileObject componentFile =
1802         JavaFileObjects.forSourceLines(
1803             "test.SimpleComponent",
1804             "package test;",
1805             "",
1806             "import dagger.Component;",
1807             "",
1808             "@Component",
1809             "interface SimpleComponent extends SimpleComponentInterface {}");
1810     JavaFileObject interfaceFile =
1811         JavaFileObjects.forSourceLines(
1812             "test.SimpleComponentInterface",
1813             "package test;",
1814             "",
1815             "interface SimpleComponentInterface {",
1816             "  GeneratedType generatedType();",
1817             "}");
1818     Compilation compilation =
1819         daggerCompiler(
1820                 new GeneratingProcessor(
1821                     "test.GeneratedType",
1822                     "package test;",
1823                     "",
1824                     "import javax.inject.Inject;",
1825                     "",
1826                     "final class GeneratedType {",
1827                     "  @Inject GeneratedType() {}",
1828                     "}"))
1829             .withOptions(compilerMode.javacopts())
1830             .compile(componentFile, interfaceFile);
1831     assertThat(compilation).succeeded();
1832     assertThat(compilation).generatedSourceFile("test.DaggerSimpleComponent");
1833   }
1834 
1835   /**
1836    * We warn when generating a {@link MembersInjector} for a type post-hoc (i.e., if Dagger wasn't
1837    * invoked when compiling the type). But Dagger only generates {@link MembersInjector}s for types
1838    * with {@link Inject @Inject} constructors if they have any injection sites, and it only
1839    * generates them for types without {@link Inject @Inject} constructors if they have local
1840    * (non-inherited) injection sites. So make sure we warn in only those cases where running the
1841    * Dagger processor actually generates a {@link MembersInjector}.
1842    */
1843   @Test
unprocessedMembersInjectorNotes()1844   public void unprocessedMembersInjectorNotes() {
1845     Compilation compilation =
1846         javac()
1847             .withOptions(
1848                 compilerMode
1849                     .javacopts()
1850                     .append(
1851                         "-Xlint:-processing",
1852                         "-Adagger.warnIfInjectionFactoryNotGeneratedUpstream=enabled"))
1853             .withProcessors(
1854                 new ElementFilteringComponentProcessor(
1855                     Predicates.not(
1856                         element ->
1857                             MoreElements.getPackage(element)
1858                                 .getQualifiedName()
1859                                 .contentEquals("test.inject"))))
1860             .compile(
1861                 JavaFileObjects.forSourceLines(
1862                     "test.TestComponent",
1863                     "package test;",
1864                     "",
1865                     "import dagger.Component;",
1866                     "",
1867                     "@Component(modules = TestModule.class)",
1868                     "interface TestComponent {",
1869                     "  void inject(test.inject.NoInjectMemberNoConstructor object);",
1870                     "  void inject(test.inject.NoInjectMemberWithConstructor object);",
1871                     "  void inject(test.inject.LocalInjectMemberNoConstructor object);",
1872                     "  void inject(test.inject.LocalInjectMemberWithConstructor object);",
1873                     "  void inject(test.inject.ParentInjectMemberNoConstructor object);",
1874                     "  void inject(test.inject.ParentInjectMemberWithConstructor object);",
1875                     "}"),
1876                 JavaFileObjects.forSourceLines(
1877                     "test.TestModule",
1878                     "package test;",
1879                     "",
1880                     "import dagger.Module;",
1881                     "import dagger.Provides;",
1882                     "",
1883                     "@Module",
1884                     "class TestModule {",
1885                     "  @Provides static Object object() {",
1886                     "    return \"object\";",
1887                     "  }",
1888                     "}"),
1889                 JavaFileObjects.forSourceLines(
1890                     "test.inject.NoInjectMemberNoConstructor",
1891                     "package test.inject;",
1892                     "",
1893                     "public class NoInjectMemberNoConstructor {",
1894                     "}"),
1895                 JavaFileObjects.forSourceLines(
1896                     "test.inject.NoInjectMemberWithConstructor",
1897                     "package test.inject;",
1898                     "",
1899                     "import javax.inject.Inject;",
1900                     "",
1901                     "public class NoInjectMemberWithConstructor {",
1902                     "  @Inject NoInjectMemberWithConstructor() {}",
1903                     "}"),
1904                 JavaFileObjects.forSourceLines(
1905                     "test.inject.LocalInjectMemberNoConstructor",
1906                     "package test.inject;",
1907                     "",
1908                     "import javax.inject.Inject;",
1909                     "",
1910                     "public class LocalInjectMemberNoConstructor {",
1911                     "  @Inject Object object;",
1912                     "}"),
1913                 JavaFileObjects.forSourceLines(
1914                     "test.inject.LocalInjectMemberWithConstructor",
1915                     "package test.inject;",
1916                     "",
1917                     "import javax.inject.Inject;",
1918                     "",
1919                     "public class LocalInjectMemberWithConstructor {",
1920                     "  @Inject LocalInjectMemberWithConstructor() {}",
1921                     "  @Inject Object object;",
1922                     "}"),
1923                 JavaFileObjects.forSourceLines(
1924                     "test.inject.ParentInjectMemberNoConstructor",
1925                     "package test.inject;",
1926                     "",
1927                     "import javax.inject.Inject;",
1928                     "",
1929                     "public class ParentInjectMemberNoConstructor",
1930                     "    extends LocalInjectMemberNoConstructor {}"),
1931                 JavaFileObjects.forSourceLines(
1932                     "test.inject.ParentInjectMemberWithConstructor",
1933                     "package test.inject;",
1934                     "",
1935                     "import javax.inject.Inject;",
1936                     "",
1937                     "public class ParentInjectMemberWithConstructor",
1938                     "    extends LocalInjectMemberNoConstructor {",
1939                     "  @Inject ParentInjectMemberWithConstructor() {}",
1940                     "}"));
1941 
1942     assertThat(compilation).succeededWithoutWarnings();
1943     assertThat(compilation)
1944         .hadNoteContaining(
1945             "Generating a MembersInjector for "
1946                 + "test.inject.LocalInjectMemberNoConstructor. "
1947                 + "Prefer to run the dagger processor over that class instead.");
1948     assertThat(compilation)
1949         .hadNoteContaining(
1950             "Generating a MembersInjector for "
1951                 + "test.inject.LocalInjectMemberWithConstructor. "
1952                 + "Prefer to run the dagger processor over that class instead.");
1953     assertThat(compilation)
1954         .hadNoteContaining(
1955             "Generating a MembersInjector for "
1956                 + "test.inject.ParentInjectMemberWithConstructor. "
1957                 + "Prefer to run the dagger processor over that class instead.");
1958     assertThat(compilation).hadNoteCount(3);
1959   }
1960 
1961   @Test
scopeAnnotationOnInjectConstructorNotValid()1962   public void scopeAnnotationOnInjectConstructorNotValid() {
1963     JavaFileObject aScope =
1964         JavaFileObjects.forSourceLines(
1965             "test.AScope",
1966             "package test;",
1967             "",
1968             "import javax.inject.Scope;",
1969             "",
1970             "@Scope",
1971             "@interface AScope {}");
1972     JavaFileObject aClass =
1973         JavaFileObjects.forSourceLines(
1974             "test.AClass",
1975             "package test;",
1976             "",
1977             "import javax.inject.Inject;",
1978             "",
1979             "final class AClass {",
1980             "  @Inject @AScope AClass() {}",
1981             "}");
1982     Compilation compilation =
1983         compilerWithOptions(compilerMode.javacopts()).compile(aScope, aClass);
1984     assertThat(compilation).failed();
1985     assertThat(compilation)
1986         .hadErrorContaining("@Scope annotations are not allowed on @Inject constructors")
1987         .inFile(aClass)
1988         .onLine(6);
1989   }
1990 
1991   @Test
unusedSubcomponents_dontResolveExtraBindingsInParentComponents()1992   public void unusedSubcomponents_dontResolveExtraBindingsInParentComponents() {
1993     JavaFileObject foo =
1994         JavaFileObjects.forSourceLines(
1995             "test.Foo",
1996             "package test;",
1997             "",
1998             "import javax.inject.Inject;",
1999             "import javax.inject.Singleton;",
2000             "",
2001             "@Singleton",
2002             "class Foo {",
2003             "  @Inject Foo() {}",
2004             "}");
2005 
2006     JavaFileObject module =
2007         JavaFileObjects.forSourceLines(
2008             "test.TestModule",
2009             "package test;",
2010             "",
2011             "import dagger.Module;",
2012             "",
2013             "@Module(subcomponents = Pruned.class)",
2014             "class TestModule {}");
2015 
2016     JavaFileObject component =
2017         JavaFileObjects.forSourceLines(
2018             "test.Parent",
2019             "package test;",
2020             "",
2021             "import dagger.Component;",
2022             "import javax.inject.Singleton;",
2023             "",
2024             "@Singleton",
2025             "@Component(modules = TestModule.class)",
2026             "interface Parent {}");
2027 
2028     JavaFileObject prunedSubcomponent =
2029         JavaFileObjects.forSourceLines(
2030             "test.Pruned",
2031             "package test;",
2032             "",
2033             "import dagger.Subcomponent;",
2034             "",
2035             "@Subcomponent",
2036             "interface Pruned {",
2037             "  @Subcomponent.Builder",
2038             "  interface Builder {",
2039             "    Pruned build();",
2040             "  }",
2041             "",
2042             "  Foo foo();",
2043             "}");
2044     JavaFileObject generated =
2045         JavaFileObjects.forSourceLines(
2046             "test.DaggerParent",
2047             "package test;",
2048             "",
2049             "import dagger.internal.Preconditions;",
2050             IMPORT_GENERATED_ANNOTATION,
2051             "",
2052             GENERATED_CODE_ANNOTATIONS,
2053             "final class DaggerParent implements Parent {",
2054             "  private DaggerParent() {",
2055             "  }",
2056             "",
2057             "  public static Builder builder() {",
2058             "    return new Builder();",
2059             "  }",
2060             "",
2061             "  public static Parent create() {",
2062             "    return new Builder().build();",
2063             "  }",
2064             "",
2065             "  static final class Builder {",
2066             "    private Builder() {}",
2067             "",
2068             "    @Deprecated",
2069             "    public Builder testModule(TestModule testModule) {",
2070             "      Preconditions.checkNotNull(testModule);",
2071             "      return this;",
2072             "    }",
2073             "",
2074             "    public Parent build() {",
2075             "      return new DaggerParent();",
2076             "    }",
2077             "  }",
2078             "}");
2079 
2080     Compilation compilation =
2081         compilerWithOptions(compilerMode.javacopts())
2082             .compile(foo, module, component, prunedSubcomponent);
2083     assertThat(compilation).succeeded();
2084     assertThat(compilation)
2085         .generatedSourceFile("test.DaggerParent")
2086         .hasSourceEquivalentTo(generated);
2087   }
2088 
2089   @Test
bindsToDuplicateBinding_bindsKeyIsNotDuplicated()2090   public void bindsToDuplicateBinding_bindsKeyIsNotDuplicated() {
2091     JavaFileObject firstModule =
2092         JavaFileObjects.forSourceLines(
2093             "test.FirstModule",
2094             "package test;",
2095             "",
2096             "import dagger.Module;",
2097             "import dagger.Provides;",
2098             "",
2099             "@Module",
2100             "abstract class FirstModule {",
2101             "  @Provides static String first() { return \"first\"; }",
2102             "}");
2103     JavaFileObject secondModule =
2104         JavaFileObjects.forSourceLines(
2105             "test.SecondModule",
2106             "package test;",
2107             "",
2108             "import dagger.Module;",
2109             "import dagger.Provides;",
2110             "",
2111             "@Module",
2112             "abstract class SecondModule {",
2113             "  @Provides static String second() { return \"second\"; }",
2114             "}");
2115     JavaFileObject bindsModule =
2116         JavaFileObjects.forSourceLines(
2117             "test.BindsModule",
2118             "package test;",
2119             "",
2120             "import dagger.Binds;",
2121             "import dagger.Module;",
2122             "",
2123             "@Module",
2124             "abstract class BindsModule {",
2125             "  @Binds abstract Object bindToDuplicateBinding(String duplicate);",
2126             "}");
2127     JavaFileObject component =
2128         JavaFileObjects.forSourceLines(
2129             "test.TestComponent",
2130             "package test;",
2131             "",
2132             "import dagger.Component;",
2133             "",
2134             "@Component(modules = {FirstModule.class, SecondModule.class, BindsModule.class})",
2135             "interface TestComponent {",
2136             "  Object notDuplicated();",
2137             "}");
2138 
2139     Compilation compilation =
2140         daggerCompiler().compile(firstModule, secondModule, bindsModule, component);
2141     assertThat(compilation).failed();
2142     assertThat(compilation).hadErrorCount(1);
2143     assertThat(compilation)
2144         .hadErrorContaining("String is bound multiple times")
2145         .inFile(component)
2146         .onLineContaining("interface TestComponent");
2147   }
2148 
2149   @Test
nullIncorrectlyReturnedFromNonNullableInlinedProvider()2150   public void nullIncorrectlyReturnedFromNonNullableInlinedProvider() {
2151     Compilation compilation =
2152         compilerWithOptions(compilerMode.javacopts())
2153             .compile(
2154                 JavaFileObjects.forSourceLines(
2155                     "test.TestModule",
2156                     "package test;",
2157                     "",
2158                     "import dagger.Module;",
2159                     "import dagger.Provides;",
2160                     "",
2161                     "@Module",
2162                     "public abstract class TestModule {",
2163                     "  @Provides static String nonNullableString() { return \"string\"; }",
2164                     "}"),
2165                 JavaFileObjects.forSourceLines(
2166                     "test.InjectsMember",
2167                     "package test;",
2168                     "",
2169                     "import javax.inject.Inject;",
2170                     "",
2171                     "public class InjectsMember {",
2172                     "  @Inject String member;",
2173                     "}"),
2174                 JavaFileObjects.forSourceLines(
2175                     "test.TestComponent",
2176                     "package test;",
2177                     "",
2178                     "import dagger.Component;",
2179                     "",
2180                     "@Component(modules = TestModule.class)",
2181                     "interface TestComponent {",
2182                     "  String nonNullableString();",
2183                     "  void inject(InjectsMember member);",
2184                     "}"));
2185     assertThat(compilation).succeededWithoutWarnings();
2186     assertThat(compilation)
2187         .generatedSourceFile("test.TestModule_NonNullableStringFactory")
2188         .containsElementsIn(
2189             JavaFileObjects.forSourceLines(
2190                 "test.TestModule_NonNullableStringFactory",
2191                 "package test;",
2192                 "",
2193                 GENERATED_CODE_ANNOTATIONS,
2194                 "public final class TestModule_NonNullableStringFactory",
2195                 "    implements Factory<String> {",
2196                 "  @Override",
2197                 "  public String get() {",
2198                 "    return nonNullableString();",
2199                 "  }",
2200                 "",
2201                 "  public static String nonNullableString() {",
2202                 "    return Preconditions.checkNotNullFromProvides(",
2203                 "        TestModule.nonNullableString());",
2204                 "  }",
2205                 "}"));
2206 
2207     JavaFileObject generatedComponent =
2208         compilerMode
2209             .javaFileBuilder("test.DaggerTestComponent")
2210             .addLines(
2211                 "package test;",
2212                 "",
2213                 GENERATED_CODE_ANNOTATIONS,
2214                 "final class DaggerTestComponent implements TestComponent {",
2215                 "  @Override",
2216                 "  public String nonNullableString() {",
2217                 "    return TestModule_NonNullableStringFactory.nonNullableString());",
2218                 "  }",
2219                 "",
2220                 "  @Override",
2221                 "  public void inject(InjectsMember member) {",
2222                 "    injectInjectsMember(member);",
2223                 "  }",
2224                 "",
2225                 "  @CanIgnoreReturnValue",
2226                 "  private InjectsMember injectInjectsMember(InjectsMember instance) {",
2227                 "    InjectsMember_MembersInjector.injectMember(instance,",
2228                 "        TestModule_NonNullableStringFactory.nonNullableString());",
2229                 "    return instance;",
2230                 "  }",
2231                 "}")
2232             .build();
2233 
2234     assertThat(compilation)
2235         .generatedSourceFile("test.DaggerTestComponent")
2236         .containsElementsIn(generatedComponent);
2237   }
2238 
2239   @Test
nullCheckingIgnoredWhenProviderReturnsPrimitive()2240   public void nullCheckingIgnoredWhenProviderReturnsPrimitive() {
2241     Compilation compilation =
2242         compilerWithOptions(compilerMode.javacopts())
2243             .compile(
2244                 JavaFileObjects.forSourceLines(
2245                     "test.TestModule",
2246                     "package test;",
2247                     "",
2248                     "import dagger.Module;",
2249                     "import dagger.Provides;",
2250                     "",
2251                     "@Module",
2252                     "public abstract class TestModule {",
2253                     "  @Provides static int primitiveInteger() { return 1; }",
2254                     "}"),
2255                 JavaFileObjects.forSourceLines(
2256                     "test.InjectsMember",
2257                     "package test;",
2258                     "",
2259                     "import javax.inject.Inject;",
2260                     "",
2261                     "public class InjectsMember {",
2262                     "  @Inject Integer member;",
2263                     "}"),
2264                 JavaFileObjects.forSourceLines(
2265                     "test.TestComponent",
2266                     "package test;",
2267                     "",
2268                     "import dagger.Component;",
2269                     "",
2270                     "@Component(modules = TestModule.class)",
2271                     "interface TestComponent {",
2272                     "  Integer nonNullableInteger();",
2273                     "  void inject(InjectsMember member);",
2274                     "}"));
2275     assertThat(compilation).succeededWithoutWarnings();
2276     assertThat(compilation)
2277         .generatedSourceFile("test.TestModule_PrimitiveIntegerFactory")
2278         .containsElementsIn(
2279             JavaFileObjects.forSourceLines(
2280                 "test.TestModule_PrimitiveIntegerFactory",
2281                 "package test;",
2282                 "",
2283                 GENERATED_CODE_ANNOTATIONS,
2284                 "public final class TestModule_PrimitiveIntegerFactory",
2285                 "    implements Factory<Integer> {",
2286                 "",
2287                 "  @Override",
2288                 "  public Integer get() {",
2289                 "    return primitiveInteger();",
2290                 "  }",
2291                 "",
2292                 "  public static int primitiveInteger() {",
2293                 "    return TestModule.primitiveInteger();",
2294                 "  }",
2295                 "}"));
2296 
2297     JavaFileObject generatedComponent =
2298         compilerMode
2299             .javaFileBuilder("test.DaggerTestComponent")
2300             .addLines(
2301                 "package test;",
2302                 "",
2303                 GENERATED_CODE_ANNOTATIONS,
2304                 "final class DaggerTestComponent implements TestComponent {",
2305                 "  @Override",
2306                 "  public Integer nonNullableInteger() {",
2307                 "    return TestModule.primitiveInteger();",
2308                 "  }",
2309                 "",
2310                 "  @Override",
2311                 "  public void inject(InjectsMember member) {",
2312                 "    injectInjectsMember(member);",
2313                 "  }",
2314                 "",
2315                 "  @CanIgnoreReturnValue",
2316                 "  private InjectsMember injectInjectsMember(InjectsMember instance) {",
2317                 "    InjectsMember_MembersInjector.injectMember(",
2318                 "        instance, TestModule.primitiveInteger());",
2319                 "    return instance;",
2320                 "  }",
2321                 "}")
2322             .build();
2323 
2324     assertThat(compilation)
2325         .generatedSourceFile("test.DaggerTestComponent")
2326         .containsElementsIn(generatedComponent);
2327   }
2328 
2329   @Test
privateMethodUsedOnlyInChildDoesNotUseQualifiedThis()2330   public void privateMethodUsedOnlyInChildDoesNotUseQualifiedThis() {
2331     JavaFileObject parent =
2332         JavaFileObjects.forSourceLines(
2333             "test.Parent",
2334             "package test;",
2335             "",
2336             "import dagger.Component;",
2337             "import javax.inject.Singleton;",
2338             "",
2339             "@Singleton",
2340             "@Component(modules=TestModule.class)",
2341             "interface Parent {",
2342             "  Child child();",
2343             "}");
2344     JavaFileObject testModule =
2345         JavaFileObjects.forSourceLines(
2346             "test.TestModule",
2347             "package test;",
2348             "",
2349             "import dagger.Module;",
2350             "import dagger.Provides;",
2351             "import javax.inject.Singleton;",
2352             "",
2353             "@Module",
2354             "abstract class TestModule {",
2355             "  @Provides @Singleton static Number number() {",
2356             "    return 3;",
2357             "  }",
2358             "",
2359             "  @Provides static String string(Number number) {",
2360             "    return number.toString();",
2361             "  }",
2362             "}");
2363     JavaFileObject child =
2364         JavaFileObjects.forSourceLines(
2365             "test.Child",
2366             "package test;",
2367             "",
2368             "import dagger.Subcomponent;",
2369             "",
2370             "@Subcomponent",
2371             "interface Child {",
2372             "  String string();",
2373             "}");
2374 
2375     JavaFileObject expectedPattern =
2376         JavaFileObjects.forSourceLines(
2377             "test.DaggerParent",
2378             "package test;",
2379             GENERATED_CODE_ANNOTATIONS,
2380             "final class DaggerParent implements Parent {",
2381             "  private String string() {",
2382             "    return TestModule_StringFactory.string(numberProvider.get());",
2383             "  }",
2384             "}");
2385 
2386     Compilation compilation = daggerCompiler().compile(parent, testModule, child);
2387     assertThat(compilation).succeededWithoutWarnings();
2388     assertThat(compilation)
2389         .generatedSourceFile("test.DaggerParent")
2390         .containsElementsIn(expectedPattern);
2391   }
2392 
2393   @Test
componentMethodInChildCallsComponentMethodInParent()2394   public void componentMethodInChildCallsComponentMethodInParent() {
2395     JavaFileObject supertype =
2396         JavaFileObjects.forSourceLines(
2397             "test.Supertype",
2398             "package test;",
2399             "",
2400             "interface Supertype {",
2401             "  String string();",
2402             "}");
2403     JavaFileObject parent =
2404         JavaFileObjects.forSourceLines(
2405             "test.Parent",
2406             "package test;",
2407             "",
2408             "import dagger.Component;",
2409             "import javax.inject.Singleton;",
2410             "",
2411             "@Singleton",
2412             "@Component(modules=TestModule.class)",
2413             "interface Parent extends Supertype {",
2414             "  Child child();",
2415             "}");
2416     JavaFileObject testModule =
2417         JavaFileObjects.forSourceLines(
2418             "test.TestModule",
2419             "package test;",
2420             "",
2421             "import dagger.Module;",
2422             "import dagger.Provides;",
2423             "import javax.inject.Singleton;",
2424             "",
2425             "@Module",
2426             "abstract class TestModule {",
2427             "  @Provides @Singleton static Number number() {",
2428             "    return 3;",
2429             "  }",
2430             "",
2431             "  @Provides static String string(Number number) {",
2432             "    return number.toString();",
2433             "  }",
2434             "}");
2435     JavaFileObject child =
2436         JavaFileObjects.forSourceLines(
2437             "test.Child",
2438             "package test;",
2439             "",
2440             "import dagger.Subcomponent;",
2441             "",
2442             "@Subcomponent",
2443             "interface Child extends Supertype {}");
2444 
2445     JavaFileObject expectedPattern =
2446         JavaFileObjects.forSourceLines(
2447             "test.DaggerParent",
2448             "package test;",
2449             GENERATED_CODE_ANNOTATIONS,
2450             "final class DaggerParent implements Parent {",
2451             "  private final class ChildImpl implements Child {",
2452             "    @Override",
2453             "    public String string() {",
2454             "      return DaggerParent.this.string();",
2455             "    }",
2456             "  }",
2457             "}");
2458 
2459     Compilation compilation = daggerCompiler().compile(supertype, parent, testModule, child);
2460     assertThat(compilation).succeededWithoutWarnings();
2461     assertThat(compilation)
2462         .generatedSourceFile("test.DaggerParent")
2463         .containsElementsIn(expectedPattern);
2464   }
2465 
2466   @Test
justInTimeAtInjectConstructor_hasGeneratedQualifier()2467   public void justInTimeAtInjectConstructor_hasGeneratedQualifier() {
2468     JavaFileObject injected =
2469         JavaFileObjects.forSourceLines(
2470             "test.Injected",
2471             "package test;",
2472             "",
2473             "import javax.inject.Inject;",
2474             "",
2475             "class Injected {",
2476             "  @Inject Injected(@GeneratedQualifier String string) {}",
2477             "}");
2478     JavaFileObject module =
2479         JavaFileObjects.forSourceLines(
2480             "test.TestModule",
2481             "package test;",
2482             "",
2483             "import dagger.Module;",
2484             "import dagger.Provides;",
2485             "",
2486             "@Module",
2487             "interface TestModule {",
2488             "  @Provides",
2489             "  static String unqualified() {",
2490             "    return new String();",
2491             "  }",
2492             "",
2493             "  @Provides",
2494             "  @GeneratedQualifier",
2495             "  static String qualified() {",
2496             "    return new String();",
2497             "  }",
2498             "}");
2499     JavaFileObject component =
2500         JavaFileObjects.forSourceLines(
2501             "test.TestComponent",
2502             "package test;",
2503             "",
2504             "import dagger.Component;",
2505             "",
2506             "@Component(modules = TestModule.class)",
2507             "interface TestComponent {",
2508             "  Injected injected();",
2509             "}");
2510 
2511     JavaFileObject generatedComponent =
2512         JavaFileObjects.forSourceLines(
2513             "test.DaggerTestComponent",
2514             "package test;",
2515             "",
2516             GENERATED_CODE_ANNOTATIONS,
2517             "final class DaggerTestComponent implements TestComponent {",
2518             "  @Override",
2519             "  public Injected injected() {",
2520             // Ensure that the qualified @Provides method is used. It's also probably more likely
2521             // that if the qualifier type hasn't been generated, a duplicate binding error will be
2522             // reported, since the annotation won't be recognized as a qualifier and instead as an
2523             // ordinary annotation.
2524             "    return new Injected(TestModule_QualifiedFactory.qualified());",
2525             "  }",
2526             "}");
2527 
2528     Compilation compilation =
2529         daggerCompiler(
2530                 new GeneratingProcessor(
2531                     "test.GeneratedQualifier",
2532                     "package test;",
2533                     "",
2534                     "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
2535                     "",
2536                     "import java.lang.annotation.Retention;",
2537                     "import javax.inject.Qualifier;",
2538                     "",
2539                     "@Retention(RUNTIME)",
2540                     "@Qualifier",
2541                     "@interface GeneratedQualifier {}"))
2542             .compile(injected, module, component);
2543     assertThat(compilation).succeededWithoutWarnings();
2544     assertThat(compilation)
2545         .generatedSourceFile("test.DaggerTestComponent")
2546         .containsElementsIn(generatedComponent);
2547   }
2548 
2549   @Test
moduleHasGeneratedQualifier()2550   public void moduleHasGeneratedQualifier() {
2551     JavaFileObject module =
2552         JavaFileObjects.forSourceLines(
2553             "test.TestModule",
2554             "package test;",
2555             "",
2556             "import dagger.Module;",
2557             "import dagger.Provides;",
2558             "",
2559             "@Module",
2560             "interface TestModule {",
2561             "  @Provides",
2562             "  static String unqualified() {",
2563             "    return new String();",
2564             "  }",
2565             "",
2566             "  @Provides",
2567             "  @GeneratedQualifier",
2568             "  static String qualified() {",
2569             "    return new String();",
2570             "  }",
2571             "}");
2572     JavaFileObject component =
2573         JavaFileObjects.forSourceLines(
2574             "test.TestComponent",
2575             "package test;",
2576             "",
2577             "import dagger.Component;",
2578             "",
2579             "@Component(modules = TestModule.class)",
2580             "interface TestComponent {",
2581             "  String unqualified();",
2582             "}");
2583 
2584     JavaFileObject generatedComponent =
2585         JavaFileObjects.forSourceLines(
2586             "test.DaggerTestComponent",
2587             "package test;",
2588             "",
2589             GENERATED_CODE_ANNOTATIONS,
2590             "final class DaggerTestComponent implements TestComponent {",
2591             "  @Override",
2592             "  public String unqualified() {",
2593             // Ensure that the unqualified @Provides method is used. It's also probably more likely
2594             // if the qualifier hasn't been generated, a duplicate binding exception will be thrown
2595             // since the annotation won't be considered a qualifier
2596             "    return TestModule_UnqualifiedFactory.unqualified();",
2597             "  }",
2598             "}");
2599 
2600     Compilation compilation =
2601         daggerCompiler(
2602             new GeneratingProcessor(
2603                 "test.GeneratedQualifier",
2604                 "package test;",
2605                 "",
2606                 "import static java.lang.annotation.RetentionPolicy.RUNTIME;",
2607                 "",
2608                 "import java.lang.annotation.Retention;",
2609                 "import javax.inject.Qualifier;",
2610                 "",
2611                 "@Retention(RUNTIME)",
2612                 "@Qualifier",
2613                 "@interface GeneratedQualifier {}"))
2614             .compile(module, component);
2615     assertThat(compilation).succeededWithoutWarnings();
2616     assertThat(compilation)
2617         .generatedSourceFile("test.DaggerTestComponent")
2618         .containsElementsIn(generatedComponent);
2619   }
2620 
2621   @Test
publicComponentType()2622   public void publicComponentType() {
2623     JavaFileObject publicComponent =
2624         JavaFileObjects.forSourceLines(
2625             "test.PublicComponent",
2626             "package test;",
2627             "",
2628             "import dagger.Component;",
2629             "",
2630             "@Component",
2631             "public interface PublicComponent {}");
2632     Compilation compilation = daggerCompiler().compile(publicComponent);
2633     assertThat(compilation).succeeded();
2634     assertThat(compilation)
2635         .generatedSourceFile("test.DaggerPublicComponent")
2636         .hasSourceEquivalentTo(
2637             JavaFileObjects.forSourceLines(
2638                 "test.DaggerPublicComponent",
2639                 "package test;",
2640                 "",
2641                 IMPORT_GENERATED_ANNOTATION,
2642                 "",
2643                 GENERATED_CODE_ANNOTATIONS,
2644                 "public final class DaggerPublicComponent implements PublicComponent {",
2645                 "  private DaggerPublicComponent() {}",
2646                 "",
2647                 "  public static Builder builder() {",
2648                 "    return new Builder();",
2649                 "  }",
2650                 "",
2651                 "  public static PublicComponent create() {",
2652                 "    return new Builder().build();",
2653                 "  }",
2654                 "",
2655                 "  public static final class Builder {",
2656                 "    private Builder() {}",
2657                 "",
2658                 "    public PublicComponent build() {",
2659                 "      return new DaggerPublicComponent();",
2660                 "    }",
2661                 "  }",
2662                 "}"));
2663   }
2664 
2665   /**
2666    * A {@link ComponentProcessor} that excludes elements using a {@link Predicate}.
2667    */
2668   private static final class ElementFilteringComponentProcessor extends AbstractProcessor {
2669     private final ComponentProcessor componentProcessor = new ComponentProcessor();
2670     private final Predicate<? super Element> filter;
2671 
2672     /**
2673      * Creates a {@link ComponentProcessor} that only processes elements that match {@code filter}.
2674      */
ElementFilteringComponentProcessor(Predicate<? super Element> filter)2675     public ElementFilteringComponentProcessor(Predicate<? super Element> filter) {
2676       this.filter = filter;
2677     }
2678 
2679     @Override
init(ProcessingEnvironment processingEnv)2680     public synchronized void init(ProcessingEnvironment processingEnv) {
2681       super.init(processingEnv);
2682       componentProcessor.init(processingEnv);
2683     }
2684 
2685     @Override
getSupportedAnnotationTypes()2686     public Set<String> getSupportedAnnotationTypes() {
2687       return componentProcessor.getSupportedAnnotationTypes();
2688     }
2689 
2690     @Override
getSupportedSourceVersion()2691     public SourceVersion getSupportedSourceVersion() {
2692       return componentProcessor.getSupportedSourceVersion();
2693     }
2694 
2695     @Override
getSupportedOptions()2696     public Set<String> getSupportedOptions() {
2697       return componentProcessor.getSupportedOptions();
2698     }
2699 
2700     @Override
process( Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv)2701     public boolean process(
2702         Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
2703       return componentProcessor.process(
2704           annotations,
2705           new RoundEnvironment() {
2706             @Override
2707             public boolean processingOver() {
2708               return roundEnv.processingOver();
2709             }
2710 
2711             @Override
2712             public Set<? extends Element> getRootElements() {
2713               return Sets.filter(roundEnv.getRootElements(), filter);
2714             }
2715 
2716             @Override
2717             public Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a) {
2718               return Sets.filter(roundEnv.getElementsAnnotatedWith(a), filter);
2719             }
2720 
2721             @Override
2722             public Set<? extends Element> getElementsAnnotatedWith(TypeElement a) {
2723               return Sets.filter(roundEnv.getElementsAnnotatedWith(a), filter);
2724             }
2725 
2726             @Override
2727             public boolean errorRaised() {
2728               return roundEnv.errorRaised();
2729             }
2730           });
2731     }
2732   }
2733 }
2734