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