• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen;
18 
19 import static com.google.common.truth.Truth.assertAbout;
20 import static com.google.testing.compile.CompilationSubject.assertThat;
21 import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
22 import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
23 import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
24 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
25 import static dagger.internal.codegen.Compilers.daggerCompiler;
26 import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
27 import static dagger.internal.codegen.GeneratedLines.IMPORT_GENERATED_ANNOTATION;
28 import static javax.tools.StandardLocation.CLASS_OUTPUT;
29 
30 import com.google.common.base.Joiner;
31 import com.google.common.collect.ImmutableList;
32 import com.google.common.collect.ImmutableSet;
33 import com.google.testing.compile.Compilation;
34 import com.google.testing.compile.JavaFileObjects;
35 import java.io.IOException;
36 import java.io.Writer;
37 import java.util.Collection;
38 import java.util.Set;
39 import javax.annotation.processing.AbstractProcessor;
40 import javax.annotation.processing.RoundEnvironment;
41 import javax.lang.model.element.TypeElement;
42 import javax.tools.JavaFileObject;
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 import org.junit.runners.Parameterized;
46 import org.junit.runners.Parameterized.Parameters;
47 
48 @RunWith(Parameterized.class)
49 public class MembersInjectionTest {
50   @Parameters(name = "{0}")
parameters()51   public static Collection<Object[]> parameters() {
52     return CompilerMode.TEST_PARAMETERS;
53   }
54 
55   private final CompilerMode compilerMode;
56 
MembersInjectionTest(CompilerMode compilerMode)57   public MembersInjectionTest(CompilerMode compilerMode) {
58     this.compilerMode = compilerMode;
59   }
60 
61   @Test
parentClass_noInjectedMembers()62   public void parentClass_noInjectedMembers() {
63     JavaFileObject childFile = JavaFileObjects.forSourceLines("test.Child",
64         "package test;",
65         "",
66         "import javax.inject.Inject;",
67         "",
68         "public final class Child extends Parent {",
69         "  @Inject Child() {}",
70         "}");
71     JavaFileObject parentFile = JavaFileObjects.forSourceLines("test.Parent",
72         "package test;",
73         "",
74         "public abstract class Parent {}");
75 
76     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
77         "package test;",
78         "",
79         "import dagger.Component;",
80         "",
81         "@Component",
82         "interface TestComponent {",
83         "  Child child();",
84         "}");
85     JavaFileObject generatedComponent =
86         JavaFileObjects.forSourceLines(
87             "test.DaggerTestComponent",
88             "package test;",
89             "",
90             GENERATED_ANNOTATION,
91             "final class DaggerTestComponent implements TestComponent {",
92             "  @Override",
93             "  public Child child() {",
94             "    return new Child();",
95             "  }",
96             "}");
97     Compilation compilation =
98         daggerCompiler()
99             .withOptions(compilerMode.javacopts())
100             .compile(childFile, parentFile, componentFile);
101 
102     assertThat(compilation).succeeded();
103     assertThat(compilation)
104         .generatedSourceFile("test.DaggerTestComponent")
105         .containsElementsIn(generatedComponent);
106   }
107 
108   @Test
parentClass_injectedMembersInSupertype()109   public void parentClass_injectedMembersInSupertype() {
110     JavaFileObject childFile = JavaFileObjects.forSourceLines("test.Child",
111         "package test;",
112         "",
113         "import javax.inject.Inject;",
114         "",
115         "public final class Child extends Parent {",
116         "  @Inject Child() {}",
117         "}");
118     JavaFileObject parentFile = JavaFileObjects.forSourceLines("test.Parent",
119         "package test;",
120         "",
121         "import javax.inject.Inject;",
122         "",
123         "public abstract class Parent {",
124         "  @Inject Dep dep;",
125         "}");
126     JavaFileObject depFile = JavaFileObjects.forSourceLines("test.Dep",
127         "package test;",
128         "",
129         "import javax.inject.Inject;",
130         "",
131         "final class Dep {",
132         "  @Inject Dep() {}",
133         "}");
134     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
135         "package test;",
136         "",
137         "import dagger.Component;",
138         "",
139         "@Component",
140         "interface TestComponent {",
141         "  Child child();",
142         "}");
143     JavaFileObject generatedComponent =
144         JavaFileObjects.forSourceLines(
145             "test.DaggerTestComponent",
146             "package test;",
147             "",
148             "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
149             "",
150             GENERATED_ANNOTATION,
151             "final class DaggerTestComponent implements TestComponent {",
152             "  @Override",
153             "  public Child child() {",
154             "    return injectChild(Child_Factory.newInstance());",
155             "  }",
156             "",
157             "  @CanIgnoreReturnValue",
158             "  private Child injectChild(Child instance) {",
159             "    Parent_MembersInjector.injectDep(instance, new Dep());",
160             "    return instance;",
161             "  }",
162             "}");
163     Compilation compilation =
164         daggerCompiler()
165             .withOptions(compilerMode.javacopts())
166             .compile(childFile, parentFile, depFile, componentFile);
167 
168     assertThat(compilation).succeeded();
169     assertThat(compilation)
170         .generatedSourceFile("test.DaggerTestComponent")
171         .containsElementsIn(generatedComponent);
172   }
173 
fieldAndMethodGenerics()174   @Test public void fieldAndMethodGenerics() {
175     JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
176         "package test;",
177         "",
178         "import javax.inject.Inject;",
179         "",
180         "class GenericClass<A, B> {",
181         "  @Inject A a;",
182         "",
183         "  @Inject GenericClass() {}",
184         "",
185         " @Inject void register(B b) {}",
186         "}");
187     JavaFileObject expected = JavaFileObjects.forSourceLines(
188         "test.GenericClass_MembersInjector",
189         "package test;",
190         "",
191         "import dagger.MembersInjector;",
192         IMPORT_GENERATED_ANNOTATION,
193         "import javax.inject.Provider;",
194         "",
195         GENERATED_ANNOTATION,
196         "public final class GenericClass_MembersInjector<A, B>",
197         "    implements MembersInjector<GenericClass<A, B>> {",
198         "  private final Provider<A> aProvider;",
199         "  private final Provider<B> bProvider;",
200         "",
201         "  public GenericClass_MembersInjector(Provider<A> aProvider, Provider<B> bProvider) {",
202         "    this.aProvider = aProvider;",
203         "    this.bProvider = bProvider;",
204         "  }",
205         "",
206         "  public static <A, B> MembersInjector<GenericClass<A, B>> create(",
207         "      Provider<A> aProvider, Provider<B> bProvider) {",
208         "    return new GenericClass_MembersInjector<A, B>(aProvider, bProvider);",
209         "  }",
210         "",
211         "  @Override",
212         "  public void injectMembers(GenericClass<A, B> instance) {",
213         "    injectA(instance, aProvider.get());",
214         "    injectRegister(instance, bProvider.get());",
215         "  }",
216         "",
217         "  public static <A, B> void injectA(Object instance, A a) {",
218         "    ((GenericClass<A, B>) instance).a = a;",
219         "  }",
220         "",
221         "  public static <A, B> void injectRegister(Object instance, B b) {",
222         "    ((GenericClass<A, B>) instance).register(b);",
223         "  }",
224         "}");
225     assertAbout(javaSource())
226         .that(file)
227         .withCompilerOptions(compilerMode.javacopts())
228         .processedWith(new ComponentProcessor())
229         .compilesWithoutError()
230         .and()
231         .generatesSources(expected);
232   }
233 
subclassedGenericMembersInjectors()234   @Test public void subclassedGenericMembersInjectors() {
235     JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
236         "package test;",
237         "",
238         "import javax.inject.Inject;",
239         "",
240         "final class A {",
241         "  @Inject A() {}",
242         "}");
243     JavaFileObject a2 = JavaFileObjects.forSourceLines("test.A2",
244         "package test;",
245         "",
246         "import javax.inject.Inject;",
247         "",
248         "final class A2 {",
249         "  @Inject A2() {}",
250         "}");
251     JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent",
252         "package test;",
253         "",
254         "import javax.inject.Inject;",
255         "",
256         "class Parent<X, Y> {",
257         "  @Inject X x;",
258         "  @Inject Y y;",
259         "  @Inject A2 a2;",
260         "",
261         "  @Inject Parent() {}",
262         "}");
263     JavaFileObject child = JavaFileObjects.forSourceLines("test.Child",
264         "package test;",
265         "",
266         "import javax.inject.Inject;",
267         "",
268         "class Child<T> extends Parent<T, A> {",
269         "  @Inject A a;",
270         "  @Inject T t;",
271         "",
272         "  @Inject Child() {}",
273         "}");
274     JavaFileObject expected = JavaFileObjects.forSourceLines(
275         "test.Child_MembersInjector",
276         "package test;",
277         "",
278         "import dagger.MembersInjector;",
279         IMPORT_GENERATED_ANNOTATION,
280         "import javax.inject.Provider;",
281         "",
282         GENERATED_ANNOTATION,
283         "public final class Child_MembersInjector<T>",
284         "    implements MembersInjector<Child<T>> {",
285         "  private final Provider<T> tAndXProvider;",
286         "  private final Provider<A> aAndYProvider;",
287         "  private final Provider<A2> a2Provider;",
288         "",
289         "  public Child_MembersInjector(",
290         "      Provider<T> tAndXProvider, Provider<A> aAndYProvider, Provider<A2> a2Provider) {",
291         "    this.tAndXProvider = tAndXProvider;",
292         "    this.aAndYProvider = aAndYProvider;",
293         "    this.a2Provider = a2Provider;",
294         "  }",
295         "",
296         "  public static <T> MembersInjector<Child<T>> create(",
297         "      Provider<T> tAndXProvider, Provider<A> aAndYProvider, Provider<A2> a2Provider) {",
298         "    return new Child_MembersInjector<T>(tAndXProvider, aAndYProvider, a2Provider);",
299         "  }",
300         "",
301         "  @Override",
302         "  public void injectMembers(Child<T> instance) {",
303         "    Parent_MembersInjector.injectX(instance, tAndXProvider.get());",
304         "    Parent_MembersInjector.injectY(instance, aAndYProvider.get());",
305         "    Parent_MembersInjector.injectA2(instance, a2Provider.get());",
306         "    injectA(instance, aAndYProvider.get());",
307         "    injectT(instance, tAndXProvider.get());",
308         "  }",
309         "",
310         "  public static <T> void injectA(Object instance, Object a) {",
311         "    ((Child<T>) instance).a = (A) a;",
312         "  }",
313         "",
314         "  public static <T> void injectT(Object instance, T t) {",
315         "    ((Child<T>) instance).t = t;",
316         "  }",
317         "}");
318     assertAbout(javaSources())
319         .that(ImmutableList.of(a, a2, parent, child))
320         .withCompilerOptions(compilerMode.javacopts())
321         .processedWith(new ComponentProcessor())
322         .compilesWithoutError()
323         .and()
324         .generatesSources(expected);
325   }
326 
fieldInjection()327   @Test public void fieldInjection() {
328     JavaFileObject file = JavaFileObjects.forSourceLines("test.FieldInjection",
329         "package test;",
330         "",
331         "import dagger.Lazy;",
332         "import javax.inject.Inject;",
333         "import javax.inject.Provider;",
334         "",
335         "class FieldInjection {",
336         "  @Inject String string;",
337         "  @Inject Lazy<String> lazyString;",
338         "  @Inject Provider<String> stringProvider;",
339         "}");
340     JavaFileObject expected =
341         JavaFileObjects.forSourceLines(
342             "test.FieldInjection_MembersInjector",
343             "package test;",
344             "",
345             "import dagger.Lazy;",
346             "import dagger.MembersInjector;",
347             "import dagger.internal.DoubleCheck;",
348             IMPORT_GENERATED_ANNOTATION,
349             "import javax.inject.Provider;",
350             "",
351             GENERATED_ANNOTATION,
352             "public final class FieldInjection_MembersInjector",
353             "    implements MembersInjector<FieldInjection> {",
354             "  private final Provider<String> stringProvider;",
355             "",
356             "  public FieldInjection_MembersInjector(Provider<String> stringProvider) {",
357             "    this.stringProvider = stringProvider;",
358             "  }",
359             "",
360             "  public static MembersInjector<FieldInjection> create(",
361             "      Provider<String> stringProvider) {",
362             "    return new FieldInjection_MembersInjector(stringProvider);",
363             "  }",
364             "",
365             "  @Override",
366             "  public void injectMembers(FieldInjection instance) {",
367             "    injectString(instance, stringProvider.get());",
368             "    injectLazyString(instance, DoubleCheck.lazy(stringProvider));",
369             "    injectStringProvider(instance, stringProvider);",
370             "  }",
371             "",
372             "  public static void injectString(Object instance, String string) {",
373             "    ((FieldInjection) instance).string = string;",
374             "  }",
375             "",
376             "  public static void injectLazyString(Object instance, Lazy<String> lazyString) {",
377             "    ((FieldInjection) instance).lazyString = lazyString;",
378             "  }",
379             "",
380             "  public static void injectStringProvider(",
381             "      Object instance, Provider<String> stringProvider) {",
382             "    ((FieldInjection) instance).stringProvider = stringProvider;",
383             "  }",
384             "}");
385     assertAbout(javaSource())
386         .that(file)
387         .withCompilerOptions(compilerMode.javacopts())
388         .processedWith(new ComponentProcessor())
389         .compilesWithoutError()
390         .and()
391         .generatesSources(expected);
392   }
393 
methodInjection()394   @Test public void methodInjection() {
395     JavaFileObject file = JavaFileObjects.forSourceLines("test.MethodInjection",
396         "package test;",
397         "",
398         "import dagger.Lazy;",
399         "import javax.inject.Inject;",
400         "import javax.inject.Provider;",
401         "",
402         "class MethodInjection {",
403         "  @Inject void noArgs() {}",
404         "  @Inject void oneArg(String string) {}",
405         "  @Inject void manyArgs(",
406         "      String string, Lazy<String> lazyString, Provider<String> stringProvider) {}",
407         "}");
408     JavaFileObject expected =
409         JavaFileObjects.forSourceLines(
410             "test.MethodInjection_MembersInjector",
411             "package test;",
412             "",
413             "import dagger.Lazy;",
414             "import dagger.MembersInjector;",
415             "import dagger.internal.DoubleCheck;",
416             IMPORT_GENERATED_ANNOTATION,
417             "import javax.inject.Provider;",
418             "",
419             GENERATED_ANNOTATION,
420             "public final class MethodInjection_MembersInjector",
421             "     implements MembersInjector<MethodInjection> {",
422             "",
423             "  private final Provider<String> stringProvider;",
424             "",
425             "  public MethodInjection_MembersInjector(Provider<String> stringProvider) {",
426             "    this.stringProvider = stringProvider;",
427             "  }",
428             "",
429             "  public static MembersInjector<MethodInjection> create(",
430             "      Provider<String> stringProvider) {",
431             "    return new MethodInjection_MembersInjector(stringProvider);",
432             "  }",
433             "",
434             "  @Override",
435             "  public void injectMembers(MethodInjection instance) {",
436             "    injectNoArgs(instance);",
437             "    injectOneArg(instance, stringProvider.get());",
438             "    injectManyArgs(",
439             "        instance,",
440             "        stringProvider.get(),",
441             "        DoubleCheck.lazy(stringProvider),",
442             "        stringProvider);",
443             "  }",
444             "",
445             "  public static void injectNoArgs(Object instance) {",
446             "    ((MethodInjection) instance).noArgs();",
447             "  }",
448             "",
449             "  public static void injectOneArg(Object instance, String string) {",
450             "    ((MethodInjection) instance).oneArg(string);",
451             "  }",
452             "",
453             "  public static void injectManyArgs(",
454             "      Object instance,",
455             "      String string,",
456             "      Lazy<String> lazyString,",
457             "      Provider<String> stringProvider) {",
458             "    ((MethodInjection) instance).manyArgs(string, lazyString, stringProvider);",
459             "  }",
460             "}");
461     assertAbout(javaSource())
462         .that(file)
463         .withCompilerOptions(compilerMode.javacopts())
464         .processedWith(new ComponentProcessor())
465         .compilesWithoutError()
466         .and()
467         .generatesSources(expected);
468   }
469 
470   @Test
mixedMemberInjection()471   public void mixedMemberInjection() {
472     JavaFileObject file = JavaFileObjects.forSourceLines(
473         "test.MixedMemberInjection",
474         "package test;",
475         "",
476         "import dagger.Lazy;",
477         "import javax.inject.Inject;",
478         "import javax.inject.Provider;",
479         "",
480         "class MixedMemberInjection {",
481         "  @Inject String string;",
482         "  @Inject void setString(String s) {}",
483         "  @Inject Object object;",
484         "  @Inject void setObject(Object o) {}",
485         "}");
486     JavaFileObject expected = JavaFileObjects.forSourceLines(
487         "test.MixedMemberInjection_MembersInjector",
488         "package test;",
489         "",
490         "import dagger.MembersInjector;",
491         IMPORT_GENERATED_ANNOTATION,
492         "import javax.inject.Provider;",
493         "",
494         GENERATED_ANNOTATION,
495         "public final class MixedMemberInjection_MembersInjector",
496         "    implements MembersInjector<MixedMemberInjection> {",
497         "",
498         "  private final Provider<String> stringAndSProvider;",
499         "  private final Provider<Object> objectAndOProvider;",
500         "",
501         "  public MixedMemberInjection_MembersInjector(",
502         "      Provider<String> stringAndSProvider,",
503         "      Provider<Object> objectAndOProvider) {",
504         "    this.stringAndSProvider = stringAndSProvider;",
505         "    this.objectAndOProvider = objectAndOProvider;",
506         "  }",
507         "",
508         "  public static MembersInjector<MixedMemberInjection> create(",
509         "      Provider<String> stringAndSProvider,",
510         "      Provider<Object> objectAndOProvider) {",
511         "    return new MixedMemberInjection_MembersInjector(",
512         "        stringAndSProvider, objectAndOProvider);",
513         "  }",
514         "",
515         "  @Override",
516         "  public void injectMembers(MixedMemberInjection instance) {",
517         "    injectString(instance, stringAndSProvider.get());",
518         "    injectObject(instance, objectAndOProvider.get());",
519         "    injectSetString(instance, stringAndSProvider.get());",
520         "    injectSetObject(instance, objectAndOProvider.get());",
521         "  }",
522         "",
523         "  public static void injectString(Object instance, String string) {",
524         "    ((MixedMemberInjection) instance).string = string;",
525         "  }",
526         "",
527         "  public static void injectObject(Object instance, Object object) {",
528         "    ((MixedMemberInjection) instance).object = object;",
529         "  }",
530         "",
531         "  public static void injectSetString(Object instance, String s) {",
532         "    ((MixedMemberInjection) instance).setString(s);",
533         "  }",
534         "",
535         "  public static void injectSetObject(Object instance, Object o) {",
536         "    ((MixedMemberInjection) instance).setObject(o);",
537         "  }",
538         "}");
539     assertAbout(javaSource())
540         .that(file)
541         .withCompilerOptions(compilerMode.javacopts())
542         .processedWith(new ComponentProcessor())
543         .compilesWithoutError()
544         .and()
545         .generatesSources(expected);
546   }
547 
injectConstructorAndMembersInjection()548   @Test public void injectConstructorAndMembersInjection() {
549     JavaFileObject file = JavaFileObjects.forSourceLines("test.AllInjections",
550         "package test;",
551         "",
552         "import javax.inject.Inject;",
553         "",
554         "class AllInjections {",
555         "  @Inject String s;",
556         "  @Inject AllInjections(String s) {}",
557         "  @Inject void s(String s) {}",
558         "}");
559     JavaFileObject expectedMembersInjector = JavaFileObjects.forSourceLines(
560         "test.AllInjections_MembersInjector",
561         "package test;",
562         "",
563         "import dagger.MembersInjector;",
564         IMPORT_GENERATED_ANNOTATION,
565         "import javax.inject.Provider;",
566         "",
567         GENERATED_ANNOTATION,
568         "public final class AllInjections_MembersInjector ",
569         "    implements MembersInjector<AllInjections> {",
570         "",
571         "  private final Provider<String> sProvider;",
572         "",
573         "  public AllInjections_MembersInjector(Provider<String> sProvider) {",
574         "    this.sProvider = sProvider;",
575         "  }",
576         "",
577         "  public static MembersInjector<AllInjections> create(Provider<String> sProvider) {",
578         "      return new AllInjections_MembersInjector(sProvider);",
579         "  }",
580         "",
581         "  @Override",
582         "  public void injectMembers(AllInjections instance) {",
583         "    injectS(instance, sProvider.get());",
584         "    injectS2(instance, sProvider.get());",
585         "  }",
586         "",
587         // TODO(b/64477506): now that these all take "object", it would be nice to rename "instance"
588         // to the type name
589         "  public static void injectS(Object instance, String s) {",
590         "    ((AllInjections) instance).s = s;",
591         "  }",
592         "",
593         "  public static void injectS2(Object instance, String s) {",
594         "    ((AllInjections) instance).s(s);",
595         "  }",
596         "",
597         "}");
598     assertAbout(javaSource())
599         .that(file)
600         .withCompilerOptions(compilerMode.javacopts())
601         .processedWith(new ComponentProcessor())
602         .compilesWithoutError()
603         .and()
604         .generatesSources(expectedMembersInjector);
605   }
606 
supertypeMembersInjection()607   @Test public void supertypeMembersInjection() {
608     JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
609         "package test;",
610         "",
611         "class A {}");
612     JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
613         "package test;",
614         "",
615         "import javax.inject.Inject;",
616         "",
617         "class B extends A {",
618         "  @Inject String s;",
619         "}");
620     JavaFileObject expectedMembersInjector = JavaFileObjects.forSourceLines(
621         "test.AllInjections_MembersInjector",
622         "package test;",
623         "",
624         "import dagger.MembersInjector;",
625         IMPORT_GENERATED_ANNOTATION,
626         "import javax.inject.Provider;",
627         "",
628         GENERATED_ANNOTATION,
629         "public final class B_MembersInjector implements MembersInjector<B> {",
630         "  private final Provider<String> sProvider;",
631         "",
632         "  public B_MembersInjector(Provider<String> sProvider) {",
633         "    this.sProvider = sProvider;",
634         "  }",
635         "",
636         "  public static MembersInjector<B> create(Provider<String> sProvider) {",
637         "      return new B_MembersInjector(sProvider);",
638         "  }",
639         "",
640         "  @Override",
641         "  public void injectMembers(B instance) {",
642         "    injectS(instance, sProvider.get());",
643         "  }",
644         "",
645         "  public static void injectS(Object instance, String s) {",
646         "    ((B) instance).s = s;",
647         "  }",
648         "}");
649     assertAbout(javaSources())
650         .that(ImmutableList.of(aFile, bFile))
651         .withCompilerOptions(compilerMode.javacopts())
652         .processedWith(new ComponentProcessor())
653         .compilesWithoutError()
654         .and()
655         .generatesSources(expectedMembersInjector);
656   }
657 
658   @Test
simpleComponentWithNesting()659   public void simpleComponentWithNesting() {
660     JavaFileObject nestedTypesFile = JavaFileObjects.forSourceLines(
661           "test.OuterType",
662           "package test;",
663           "",
664           "import dagger.Component;",
665           "import javax.inject.Inject;",
666           "",
667           "final class OuterType {",
668           "  static class A {",
669           "    @Inject A() {}",
670           "  }",
671           "  static class B {",
672           "    @Inject A a;",
673           "  }",
674           "  @Component interface SimpleComponent {",
675           "    A a();",
676           "    void inject(B b);",
677           "  }",
678           "}");
679     JavaFileObject bMembersInjector = JavaFileObjects.forSourceLines(
680           "test.OuterType_B_MembersInjector",
681           "package test;",
682           "",
683           "import dagger.MembersInjector;",
684           IMPORT_GENERATED_ANNOTATION,
685           "import javax.inject.Provider;",
686           "",
687           GENERATED_ANNOTATION,
688           "public final class OuterType_B_MembersInjector",
689           "    implements MembersInjector<OuterType.B> {",
690           "  private final Provider<OuterType.A> aProvider;",
691           "",
692           "  public OuterType_B_MembersInjector(Provider<OuterType.A> aProvider) {",
693           "    this.aProvider = aProvider;",
694           "  }",
695           "",
696           "  public static MembersInjector<OuterType.B> create(Provider<OuterType.A> aProvider) {",
697           "    return new OuterType_B_MembersInjector(aProvider);",
698           "  }",
699           "",
700           "  @Override",
701           "  public void injectMembers(OuterType.B instance) {",
702           "    injectA(instance, aProvider.get());",
703           "  }",
704           "",
705           "  public static void injectA(Object instance, Object a) {",
706           "    ((OuterType.B) instance).a = (OuterType.A) a;",
707           "  }",
708           "}");
709     assertAbout(javaSources())
710         .that(ImmutableList.of(nestedTypesFile))
711         .withCompilerOptions(compilerMode.javacopts())
712         .processedWith(new ComponentProcessor())
713         .compilesWithoutError()
714         .and()
715         .generatesSources(bMembersInjector);
716   }
717 
718   @Test
componentWithNestingAndGeneratedType()719   public void componentWithNestingAndGeneratedType() {
720     JavaFileObject nestedTypesFile =
721         JavaFileObjects.forSourceLines(
722             "test.OuterType",
723             "package test;",
724             "",
725             "import dagger.Component;",
726             "import javax.inject.Inject;",
727             "",
728             "final class OuterType {",
729             "  @Inject GeneratedType generated;",
730             "  static class A {",
731             "    @Inject A() {}",
732             "  }",
733             "  static class B {",
734             "    @Inject A a;",
735             "  }",
736             "  @Component interface SimpleComponent {",
737             "    A a();",
738             "    void inject(B b);",
739             "  }",
740             "}");
741     JavaFileObject bMembersInjector =
742         JavaFileObjects.forSourceLines(
743             "test.OuterType_B_MembersInjector",
744             "package test;",
745             "",
746             "import dagger.MembersInjector;",
747             IMPORT_GENERATED_ANNOTATION,
748             "import javax.inject.Provider;",
749             "",
750             GENERATED_ANNOTATION,
751             "public final class OuterType_B_MembersInjector",
752             "    implements MembersInjector<OuterType.B> {",
753             "  private final Provider<OuterType.A> aProvider;",
754             "",
755             "  public OuterType_B_MembersInjector(Provider<OuterType.A> aProvider) {",
756             "    this.aProvider = aProvider;",
757             "  }",
758             "",
759             "  public static MembersInjector<OuterType.B> create(",
760             "      Provider<OuterType.A> aProvider) {",
761             "    return new OuterType_B_MembersInjector(aProvider);",
762             "  }",
763             "",
764             "  @Override",
765             "  public void injectMembers(OuterType.B instance) {",
766             "    injectA(instance, aProvider.get());",
767             "  }",
768             "",
769             "  public static void injectA(Object instance, Object a) {",
770             "    ((OuterType.B) instance).a = (OuterType.A) a;",
771             "  }",
772             "}");
773     assertAbout(javaSource())
774         .that(nestedTypesFile)
775         .withCompilerOptions(compilerMode.javacopts())
776         .processedWith(
777             new ComponentProcessor(),
778             new AbstractProcessor() {
779               private boolean done;
780 
781               @Override
782               public Set<String> getSupportedAnnotationTypes() {
783                 return ImmutableSet.of("*");
784               }
785 
786               @Override
787               public boolean process(
788                   Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
789                 if (!done) {
790                   done = true;
791                   try (Writer writer =
792                       processingEnv
793                           .getFiler()
794                           .createSourceFile("test.GeneratedType")
795                           .openWriter()) {
796                     writer.write(
797                         Joiner.on('\n')
798                             .join(
799                                 "package test;",
800                                 "",
801                                 "import javax.inject.Inject;",
802                                 "",
803                                 "class GeneratedType {",
804                                 "  @Inject GeneratedType() {}",
805                                 "}"));
806                   } catch (IOException e) {
807                     throw new RuntimeException(e);
808                   }
809                 }
810                 return false;
811               }
812             })
813         .compilesWithoutError()
814         .and()
815         .generatesSources(bMembersInjector);
816   }
817 
818   @Test
lowerCaseNamedMembersInjector_forLowerCaseType()819   public void lowerCaseNamedMembersInjector_forLowerCaseType() {
820     JavaFileObject foo =
821         JavaFileObjects.forSourceLines(
822             "test.foo",
823             "package test;",
824             "",
825             "import javax.inject.Inject;",
826             "",
827             "class foo {",
828             "  @Inject String string;",
829             "}");
830     JavaFileObject fooModule =
831         JavaFileObjects.forSourceLines(
832             "test.fooModule",
833             "package test;",
834             "",
835             "import dagger.Module;",
836             "import dagger.Provides;",
837             "",
838             "@Module",
839             "class fooModule {",
840             "  @Provides String string() { return \"foo\"; }",
841             "}");
842     JavaFileObject fooComponent =
843         JavaFileObjects.forSourceLines(
844             "test.fooComponent",
845             "package test;",
846             "",
847             "import dagger.Component;",
848             "",
849             "@Component(modules = fooModule.class)",
850             "interface fooComponent {",
851             "  void inject(foo target);",
852             "}");
853 
854     Compilation compilation =
855         daggerCompiler()
856             .withOptions(compilerMode.javacopts())
857             .compile(foo, fooModule, fooComponent);
858     assertThat(compilation).succeeded();
859     assertThat(compilation).generatedFile(CLASS_OUTPUT, "test", "foo_MembersInjector.class");
860   }
861 
862   @Test
fieldInjectionForShadowedMember()863   public void fieldInjectionForShadowedMember() {
864     JavaFileObject foo =
865         JavaFileObjects.forSourceLines(
866             "test.Foo",
867             "package test;",
868             "",
869             "import javax.inject.Inject;",
870             "",
871             "class Foo {",
872             "  @Inject Foo() {}",
873             "}");
874     JavaFileObject bar =
875         JavaFileObjects.forSourceLines(
876             "test.Bar",
877             "package test;",
878             "",
879             "import javax.inject.Inject;",
880             "",
881             "class Bar {",
882             "  @Inject Bar() {}",
883             "}");
884     JavaFileObject parent =
885         JavaFileObjects.forSourceLines(
886             "test.Parent",
887             "package test;",
888             "",
889             "import javax.inject.Inject;",
890             "",
891             "class Parent { ",
892             "  @Inject Foo object;",
893             "}");
894     JavaFileObject child =
895         JavaFileObjects.forSourceLines(
896             "test.Child",
897             "package test;",
898             "",
899             "import javax.inject.Inject;",
900             "",
901             "class Child extends Parent { ",
902             "  @Inject Bar object;",
903             "}");
904     JavaFileObject component =
905         JavaFileObjects.forSourceLines(
906             "test.C",
907             "package test;",
908             "",
909             "import dagger.Component;",
910             "",
911             "@Component",
912             "interface C { ",
913             "  void inject(Child child);",
914             "}");
915 
916     JavaFileObject expectedMembersInjector =
917         JavaFileObjects.forSourceLines(
918             "test.Child_MembersInjector",
919             "package test;",
920             "",
921             "import dagger.MembersInjector;",
922             IMPORT_GENERATED_ANNOTATION,
923             "import javax.inject.Provider;",
924             "",
925             GENERATED_ANNOTATION,
926             "public final class Child_MembersInjector implements MembersInjector<Child> {",
927             "  private final Provider<Foo> objectProvider;",
928             "  private final Provider<Bar> objectProvider2;",
929             "",
930             "  public Child_MembersInjector(",
931             "        Provider<Foo> objectProvider, Provider<Bar> objectProvider2) {",
932             "    this.objectProvider = objectProvider;",
933             "    this.objectProvider2 = objectProvider2;",
934             "  }",
935             "",
936             "  public static MembersInjector<Child> create(",
937             "      Provider<Foo> objectProvider, Provider<Bar> objectProvider2) {",
938             "    return new Child_MembersInjector(objectProvider, objectProvider2);",
939             "  }",
940             "",
941             "  @Override",
942             "  public void injectMembers(Child instance) {",
943             "    Parent_MembersInjector.injectObject(instance, objectProvider.get());",
944             "    injectObject(instance, objectProvider2.get());",
945             "  }",
946             "",
947             "  public static void injectObject(Object instance, Object object) {",
948             "    ((Child) instance).object = (Bar) object;",
949             "  }",
950             "}");
951 
952     assertAbout(javaSources())
953         .that(ImmutableList.of(foo, bar, parent, child, component))
954         .withCompilerOptions(compilerMode.javacopts())
955         .processedWith(new ComponentProcessor())
956         .compilesWithoutError()
957         .and()
958         .generatesSources(expectedMembersInjector);
959   }
960 
privateNestedClassError()961   @Test public void privateNestedClassError() {
962     JavaFileObject file = JavaFileObjects.forSourceLines("test.OuterClass",
963         "package test;",
964         "",
965         "import javax.inject.Inject;",
966         "",
967         "final class OuterClass {",
968         "  private static final class InnerClass {",
969         "    @Inject int field;",
970         "  }",
971         "}");
972     Compilation compilation = daggerCompiler().withOptions(compilerMode.javacopts()).compile(file);
973     assertThat(compilation).failed();
974     assertThat(compilation)
975         .hadErrorContaining("Dagger does not support injection into private classes")
976         .inFile(file)
977         .onLine(6);
978   }
979 
privateNestedClassWarning()980   @Test public void privateNestedClassWarning() {
981     JavaFileObject file = JavaFileObjects.forSourceLines("test.OuterClass",
982         "package test;",
983         "",
984         "import javax.inject.Inject;",
985         "",
986         "final class OuterClass {",
987         "  private static final class InnerClass {",
988         "    @Inject int field;",
989         "  }",
990         "}");
991     Compilation compilation =
992         daggerCompiler()
993             .withOptions(
994                 compilerMode.javacopts().append("-Adagger.privateMemberValidation=WARNING"))
995             .compile(file);
996     assertThat(compilation).succeeded();
997     assertThat(compilation)
998         .hadWarningContaining("Dagger does not support injection into private classes")
999         .inFile(file)
1000         .onLine(6);
1001   }
1002 
privateSuperclassIsOkIfNotInjectedInto()1003   @Test public void privateSuperclassIsOkIfNotInjectedInto() {
1004     JavaFileObject file = JavaFileObjects.forSourceLines("test.OuterClass",
1005         "package test;",
1006         "",
1007         "import javax.inject.Inject;",
1008         "",
1009         "final class OuterClass {",
1010         "  private static class BaseClass {}",
1011         "",
1012         "  static final class DerivedClass extends BaseClass {",
1013         "    @Inject int field;",
1014         "  }",
1015         "}");
1016     Compilation compilation = daggerCompiler().withOptions(compilerMode.javacopts()).compile(file);
1017     assertThat(compilation).succeeded();
1018   }
1019 
1020   @Test
rawFrameworkTypeField()1021   public void rawFrameworkTypeField() {
1022     JavaFileObject file =
1023         JavaFileObjects.forSourceLines(
1024             "test.RawFrameworkTypes",
1025             "package test;",
1026             "",
1027             "import dagger.Component;",
1028             "import javax.inject.Inject;",
1029             "import javax.inject.Provider;",
1030             "",
1031             "class RawProviderField {",
1032             "  @Inject Provider fieldWithRawProvider;",
1033             "}",
1034             "",
1035             "@Component",
1036             "interface C {",
1037             "  void inject(RawProviderField rawProviderField);",
1038             "}");
1039 
1040     Compilation compilation = daggerCompiler().withOptions(compilerMode.javacopts()).compile(file);
1041     assertThat(compilation).failed();
1042     assertThat(compilation)
1043         .hadErrorContaining("javax.inject.Provider cannot be provided")
1044         .inFile(file)
1045         .onLineContaining("interface C");
1046   }
1047 
1048   @Test
rawFrameworkTypeParameter()1049   public void rawFrameworkTypeParameter() {
1050     JavaFileObject file =
1051         JavaFileObjects.forSourceLines(
1052             "test.RawFrameworkTypes",
1053             "package test;",
1054             "",
1055             "import dagger.Component;",
1056             "import javax.inject.Inject;",
1057             "import javax.inject.Provider;",
1058             "",
1059             "class RawProviderParameter {",
1060             "  @Inject void methodInjection(Provider rawProviderParameter) {}",
1061             "}",
1062             "",
1063             "@Component",
1064             "interface C {",
1065             "  void inject(RawProviderParameter rawProviderParameter);",
1066             "}");
1067 
1068     Compilation compilation = daggerCompiler().withOptions(compilerMode.javacopts()).compile(file);
1069     assertThat(compilation).failed();
1070     assertThat(compilation)
1071         .hadErrorContaining("javax.inject.Provider cannot be provided")
1072         .inFile(file)
1073         .onLineContaining("interface C");
1074   }
1075 
1076   @Test
injectsPrimitive()1077   public void injectsPrimitive() {
1078     JavaFileObject injectedType =
1079         JavaFileObjects.forSourceLines(
1080             "test.InjectedType",
1081             "package test;",
1082             "",
1083             "import javax.inject.Inject;",
1084             "",
1085             "class InjectedType {",
1086             "  @Inject InjectedType() {}",
1087             "",
1088             "  @Inject int primitiveInt;",
1089             "  @Inject Integer boxedInt;",
1090             "}");
1091     JavaFileObject membersInjector =
1092         JavaFileObjects.forSourceLines(
1093             "test.InjectedType_MembersInjector",
1094             "package test;",
1095             "",
1096             "import dagger.MembersInjector;",
1097             IMPORT_GENERATED_ANNOTATION,
1098             "import javax.inject.Provider;",
1099             "",
1100             GENERATED_ANNOTATION,
1101             "public final class InjectedType_MembersInjector ",
1102             "    implements MembersInjector<InjectedType> {",
1103             "  private final Provider<Integer> boxedIntAndPrimitiveIntProvider;",
1104             "",
1105             "  public InjectedType_MembersInjector(",
1106             "      Provider<Integer> boxedIntAndPrimitiveIntProvider) {",
1107             "    this.boxedIntAndPrimitiveIntProvider = boxedIntAndPrimitiveIntProvider;",
1108             "  }",
1109             "",
1110             "  public static MembersInjector<InjectedType> create(",
1111             "      Provider<Integer> boxedIntAndPrimitiveIntProvider) {",
1112             "    return new InjectedType_MembersInjector(boxedIntAndPrimitiveIntProvider);",
1113             "  }",
1114             "",
1115             "  @Override",
1116             "  public void injectMembers(InjectedType instance) {",
1117             "    injectPrimitiveInt(instance, boxedIntAndPrimitiveIntProvider.get());",
1118             "    injectBoxedInt(instance, boxedIntAndPrimitiveIntProvider.get());",
1119             "  }",
1120             "",
1121             "  public static void injectPrimitiveInt(Object instance, int primitiveInt) {",
1122             "    ((InjectedType) instance).primitiveInt = primitiveInt;",
1123             "  }",
1124             "",
1125             "  public static void injectBoxedInt(Object instance, Integer boxedInt) {",
1126             "    ((InjectedType) instance).boxedInt = boxedInt;",
1127             "  }",
1128             "}");
1129     JavaFileObject factory =
1130         JavaFileObjects.forSourceLines(
1131             "test.InjectedType_Factory",
1132             "package test;",
1133             "",
1134             "import dagger.internal.Factory;",
1135             IMPORT_GENERATED_ANNOTATION,
1136             "import javax.inject.Provider;",
1137             "",
1138             GENERATED_ANNOTATION,
1139             "public final class InjectedType_Factory implements Factory<InjectedType> {",
1140             "  private final Provider<Integer> boxedIntAndPrimitiveIntProvider;",
1141             "",
1142             "  public InjectedType_Factory(Provider<Integer> boxedIntAndPrimitiveIntProvider) {",
1143             "    this.boxedIntAndPrimitiveIntProvider = boxedIntAndPrimitiveIntProvider;",
1144             "  }",
1145             "",
1146             "  @Override",
1147             "  public InjectedType get() {",
1148             "    InjectedType instance = new InjectedType();",
1149             "    InjectedType_MembersInjector.injectPrimitiveInt(",
1150             "        instance, boxedIntAndPrimitiveIntProvider.get());",
1151             "    InjectedType_MembersInjector.injectBoxedInt(",
1152             "        instance, boxedIntAndPrimitiveIntProvider.get());",
1153             "    return instance;",
1154             "  }",
1155             "",
1156             "  public static InjectedType_Factory create(",
1157             "      Provider<Integer> boxedIntAndPrimitiveIntProvider) {",
1158             "    return new InjectedType_Factory(boxedIntAndPrimitiveIntProvider);",
1159             "  }",
1160             "",
1161             "  public static InjectedType newInstance() {",
1162             "    return new InjectedType();",
1163             "  }",
1164             "}");
1165     Compilation compilation =
1166         daggerCompiler().withOptions(compilerMode.javacopts()).compile(injectedType);
1167     assertThat(compilation).succeeded();
1168     assertThat(compilation)
1169         .generatedSourceFile("test.InjectedType_MembersInjector")
1170         .hasSourceEquivalentTo(membersInjector);
1171     assertThat(compilation)
1172         .generatedSourceFile("test.InjectedType_Factory")
1173         .hasSourceEquivalentTo(factory);
1174   }
1175 
1176   @Test
accessibility()1177   public void accessibility() {
1178     JavaFileObject foo =
1179         JavaFileObjects.forSourceLines(
1180             "other.Foo",
1181             "package other;",
1182             "",
1183             "import javax.inject.Inject;",
1184             "",
1185             "class Foo {",
1186             "  @Inject Foo() {}",
1187             "}");
1188     JavaFileObject inaccessible =
1189         JavaFileObjects.forSourceLines(
1190             "other.Inaccessible",
1191             "package other;",
1192             "",
1193             "import javax.inject.Inject;",
1194             "",
1195             "class Inaccessible {",
1196             "  @Inject Inaccessible() {}",
1197             "  @Inject Foo foo;",
1198             "  @Inject void method(Foo foo) {}",
1199             "}");
1200     JavaFileObject usesInaccessible =
1201         JavaFileObjects.forSourceLines(
1202             "other.UsesInaccessible",
1203             "package other;",
1204             "",
1205             "import javax.inject.Inject;",
1206             "",
1207             "public class UsesInaccessible {",
1208             "  @Inject UsesInaccessible(Inaccessible inaccessible) {}",
1209             "}");
1210     JavaFileObject component =
1211         JavaFileObjects.forSourceLines(
1212             "test.TestComponent",
1213             "package test;",
1214             "",
1215             "import dagger.Component;",
1216             "import other.UsesInaccessible;",
1217             "",
1218             "@Component",
1219             "interface TestComponent {",
1220             "  UsesInaccessible usesInaccessible();",
1221             "}");
1222 
1223     Compilation compilation =
1224         daggerCompiler()
1225             .withOptions(compilerMode.javacopts())
1226             .compile(foo, inaccessible, usesInaccessible, component);
1227     assertThat(compilation).succeeded();
1228     assertThat(compilation)
1229         .generatedSourceFile("other.Inaccessible_MembersInjector")
1230         .hasSourceEquivalentTo(
1231             JavaFileObjects.forSourceLines(
1232                 "other.Inaccessible_MembersInjector",
1233                 "package other;",
1234                 "",
1235                 "import dagger.MembersInjector;",
1236                 IMPORT_GENERATED_ANNOTATION,
1237                 "import javax.inject.Provider;",
1238                 "",
1239                 GENERATED_ANNOTATION,
1240                 "public final class Inaccessible_MembersInjector",
1241                 "    implements MembersInjector<Inaccessible> {",
1242                 "  private final Provider<Foo> fooProvider;",
1243                 "",
1244                 "  public Inaccessible_MembersInjector(Provider<Foo> fooProvider) {",
1245                 "    this.fooProvider = fooProvider;",
1246                 "  }",
1247                 "",
1248                 "  public static MembersInjector<Inaccessible> create(Provider<Foo> fooProvider) {",
1249                 "    return new Inaccessible_MembersInjector(fooProvider);",
1250                 "  }",
1251                 "",
1252                 "  @Override",
1253                 "  public void injectMembers(Inaccessible instance) {",
1254                 "    injectFoo(instance, fooProvider.get());",
1255                 "    injectMethod(instance, fooProvider.get());",
1256                 "  }",
1257                 "",
1258                 "  public static void injectFoo(Object instance, Object foo) {",
1259                 "    ((Inaccessible) instance).foo = (Foo) foo;",
1260                 "  }",
1261                 "",
1262                 "  public static void injectMethod(Object instance, Object foo) {",
1263                 "    ((Inaccessible) instance).method((Foo) foo);",
1264                 "  }",
1265                 "}"));
1266     JavaFileObject generatedComponent =
1267         JavaFileObjects.forSourceLines(
1268             "test.DaggerTestComponent",
1269             "package test;",
1270             "",
1271             "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
1272             IMPORT_GENERATED_ANNOTATION,
1273             "import other.Foo_Factory;",
1274             "import other.Inaccessible_Factory;",
1275             "import other.Inaccessible_MembersInjector;",
1276             "import other.UsesInaccessible;",
1277             "import other.UsesInaccessible_Factory;",
1278             "",
1279             GENERATED_ANNOTATION,
1280             "final class DaggerTestComponent implements TestComponent {",
1281             "  private Object getInaccessible() {",
1282             "    return injectInaccessible(Inaccessible_Factory.newInstance());",
1283             "  }",
1284             "",
1285             "  @Override",
1286             "  public UsesInaccessible usesInaccessible() {",
1287             "    return UsesInaccessible_Factory.newInstance(",
1288             "        getInaccessible());",
1289             "  }",
1290             "",
1291             // TODO(ronshapiro): if possible, it would be great to rename "instance", but we
1292             // need to make sure that this doesn't conflict with any framework field in this or
1293             // any parent component
1294             "  @CanIgnoreReturnValue",
1295             "  private Object injectInaccessible(Object instance) {",
1296             "    Inaccessible_MembersInjector.injectFoo(instance, Foo_Factory.newInstance());",
1297             "    Inaccessible_MembersInjector.injectMethod(instance, Foo_Factory.newInstance());",
1298             "    return instance;",
1299             "  }",
1300             "}");
1301     assertThat(compilation)
1302         .generatedSourceFile("test.DaggerTestComponent")
1303         .containsElementsIn(generatedComponent);
1304   }
1305 
1306   @Test
accessibleRawType_ofInaccessibleType()1307   public void accessibleRawType_ofInaccessibleType() {
1308     JavaFileObject inaccessible =
1309         JavaFileObjects.forSourceLines(
1310             "other.Inaccessible",
1311             "package other;",
1312             "",
1313             "class Inaccessible {}");
1314     JavaFileObject inaccessiblesModule =
1315         JavaFileObjects.forSourceLines(
1316             "other.InaccessiblesModule",
1317             "package other;",
1318             "",
1319             "import dagger.Module;",
1320             "import dagger.Provides;",
1321             "import java.util.ArrayList;",
1322             "import java.util.List;",
1323             "import javax.inject.Provider;",
1324             "import javax.inject.Singleton;",
1325             "",
1326             "@Module",
1327             "public class InaccessiblesModule {",
1328             // force Provider initialization
1329             "  @Provides @Singleton static List<Inaccessible> inaccessibles() {",
1330             "    return new ArrayList<>();",
1331             "  }",
1332             "}");
1333     JavaFileObject usesInaccessibles =
1334         JavaFileObjects.forSourceLines(
1335             "other.UsesInaccessibles",
1336             "package other;",
1337             "",
1338             "import java.util.List;",
1339             "import javax.inject.Inject;",
1340             "",
1341             "public class UsesInaccessibles {",
1342             "  @Inject UsesInaccessibles() {}",
1343             "  @Inject List<Inaccessible> inaccessibles;",
1344             "}");
1345     JavaFileObject component =
1346         JavaFileObjects.forSourceLines(
1347             "test.TestComponent",
1348             "package test;",
1349             "",
1350             "import dagger.Component;",
1351             "import javax.inject.Singleton;",
1352             "import other.UsesInaccessibles;",
1353             "",
1354             "@Singleton",
1355             "@Component(modules = other.InaccessiblesModule.class)",
1356             "interface TestComponent {",
1357             "  UsesInaccessibles usesInaccessibles();",
1358             "}");
1359 
1360     Compilation compilation =
1361         daggerCompiler()
1362             .withOptions(compilerMode.javacopts())
1363             .compile(inaccessible, inaccessiblesModule, usesInaccessibles, component);
1364     assertThat(compilation).succeeded();
1365     JavaFileObject generatedComponent =
1366         compilerMode
1367             .javaFileBuilder("test.DaggerTestComponent")
1368             .addLines(
1369                 "package test;",
1370                 "",
1371                 "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
1372                 "import other.InaccessiblesModule;",
1373                 "import other.InaccessiblesModule_InaccessiblesFactory;",
1374                 "import other.UsesInaccessibles;",
1375                 "import other.UsesInaccessibles_Factory;",
1376                 "import other.UsesInaccessibles_MembersInjector;",
1377                 "",
1378                 GENERATED_ANNOTATION,
1379                 "final class DaggerTestComponent implements TestComponent {")
1380             .addLinesIn(
1381                 FAST_INIT_MODE,
1382                 "  private volatile Object listOfInaccessible = new MemoizedSentinel();",
1383                 "",
1384                 "  private List getListOfInaccessible() {",
1385                 "    Object local = listOfInaccessible;",
1386                 "    if (local instanceof MemoizedSentinel) {",
1387                 "      synchronized (local) {",
1388                 "        local = listOfInaccessible;",
1389                 "        if (local instanceof MemoizedSentinel) {",
1390                 "          local = InaccessiblesModule_InaccessiblesFactory.inaccessibles();",
1391                 "          listOfInaccessible =",
1392                 "              DoubleCheck.reentrantCheck(listOfInaccessible, local);",
1393                 "        }",
1394                 "      }",
1395                 "    }",
1396                 "    return (List) local;",
1397                 "  }")
1398             .addLinesIn(
1399                 DEFAULT_MODE,
1400                 "  @SuppressWarnings(\"rawtypes\")",
1401                 "  private Provider inaccessiblesProvider;",
1402                 "",
1403                 "  @SuppressWarnings(\"unchecked\")",
1404                 "  private void initialize() {",
1405                 "    this.inaccessiblesProvider =",
1406                 "        DoubleCheck.provider(InaccessiblesModule_InaccessiblesFactory.create());",
1407                 "  }")
1408             .addLines(
1409                 "",
1410                 "  @Override",
1411                 "  public UsesInaccessibles usesInaccessibles() {",
1412                 "    return injectUsesInaccessibles(",
1413                 "        UsesInaccessibles_Factory.newInstance());",
1414                 "  }",
1415                 "",
1416                 "  @CanIgnoreReturnValue",
1417                 "  private UsesInaccessibles injectUsesInaccessibles(",
1418                 "        UsesInaccessibles instance) {",
1419                 "    UsesInaccessibles_MembersInjector.injectInaccessibles(")
1420             .addLinesIn(
1421                 FAST_INIT_MODE,
1422                 "        instance, (List) getListOfInaccessible());")
1423             .addLinesIn(
1424                 DEFAULT_MODE,
1425                 "        instance, (List) inaccessiblesProvider.get());")
1426             .addLines(
1427                 "    return instance;",
1428                 "  }",
1429                 "}")
1430             .build();
1431     assertThat(compilation)
1432         .generatedSourceFile("test.DaggerTestComponent")
1433         .containsElementsIn(generatedComponent);
1434   }
1435 
1436   @Test
publicSupertypeHiddenSubtype()1437   public void publicSupertypeHiddenSubtype() {
1438     JavaFileObject foo =
1439         JavaFileObjects.forSourceLines(
1440             "other.Foo",
1441             "package other;",
1442             "",
1443             "import javax.inject.Inject;",
1444             "",
1445             "class Foo {",
1446             "  @Inject Foo() {}",
1447             "}");
1448     JavaFileObject supertype =
1449         JavaFileObjects.forSourceLines(
1450             "other.Supertype",
1451             "package other;",
1452             "",
1453             "import javax.inject.Inject;",
1454             "",
1455             "public class Supertype<T> {",
1456             "  @Inject T t;",
1457             "}");
1458     JavaFileObject subtype =
1459         JavaFileObjects.forSourceLines(
1460             "other.Subtype",
1461             "package other;",
1462             "",
1463             "import javax.inject.Inject;",
1464             "",
1465             "class Subtype extends Supertype<Foo> {",
1466             "  @Inject Subtype() {}",
1467             "}");
1468     JavaFileObject injectsSubtype =
1469         JavaFileObjects.forSourceLines(
1470             "other.InjectsSubtype",
1471             "package other;",
1472             "",
1473             "import javax.inject.Inject;",
1474             "",
1475             "public class InjectsSubtype {",
1476             "  @Inject InjectsSubtype(Subtype s) {}",
1477             "}");
1478     JavaFileObject component =
1479         JavaFileObjects.forSourceLines(
1480             "test.TestComponent",
1481             "package test;",
1482             "",
1483             "import dagger.Component;",
1484             "",
1485             "@Component",
1486             "interface TestComponent {",
1487             "  other.InjectsSubtype injectsSubtype();",
1488             "}");
1489 
1490     Compilation compilation =
1491         daggerCompiler()
1492             .withOptions(compilerMode.javacopts())
1493             .compile(foo, supertype, subtype, injectsSubtype, component);
1494     assertThat(compilation).succeeded();
1495     JavaFileObject generatedComponent =
1496         JavaFileObjects.forSourceLines(
1497             "test.DaggerTestComponent",
1498             "package test;",
1499             "",
1500             "import com.google.errorprone.annotations.CanIgnoreReturnValue;",
1501             "import other.Foo_Factory;",
1502             "import other.InjectsSubtype;",
1503             "import other.InjectsSubtype_Factory;",
1504             "import other.Subtype_Factory;",
1505             "import other.Supertype;",
1506             "import other.Supertype_MembersInjector;",
1507             "",
1508             GENERATED_ANNOTATION,
1509             "final class DaggerTestComponent implements TestComponent {",
1510             "  private Object getSubtype() {",
1511             "    return injectSubtype(Subtype_Factory.newInstance());",
1512             "  }",
1513             "",
1514             "  @Override",
1515             "  public InjectsSubtype injectsSubtype() {",
1516             "    return InjectsSubtype_Factory.newInstance(getSubtype());",
1517             "  }",
1518             "",
1519             "  @CanIgnoreReturnValue",
1520             "  private Object injectSubtype(Object instance) {",
1521             "    Supertype_MembersInjector.injectT(",
1522             "        (Supertype) instance, Foo_Factory.newInstance());",
1523             "    return instance;",
1524             "  }",
1525             "}");
1526 
1527     assertThat(compilation)
1528         .generatedSourceFile("test.DaggerTestComponent")
1529         .containsElementsIn(generatedComponent);
1530   }
1531 }
1532