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