• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 dagger.internal.codegen.CompilerMode.DEFAULT_MODE;
21 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
22 import static dagger.internal.codegen.Compilers.daggerCompiler;
23 import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION;
24 
25 import com.google.testing.compile.Compilation;
26 import com.google.testing.compile.CompilationSubject;
27 import com.google.testing.compile.JavaFileObjects;
28 import java.util.Collection;
29 import javax.tools.JavaFileObject;
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 import org.junit.runners.Parameterized;
33 import org.junit.runners.Parameterized.Parameters;
34 
35 @RunWith(Parameterized.class)
36 public class DelegateBindingExpressionTest {
37   @Parameters(name = "{0}")
parameters()38   public static Collection<Object[]> parameters() {
39     return CompilerMode.TEST_PARAMETERS;
40   }
41 
42   private final CompilerMode compilerMode;
43 
DelegateBindingExpressionTest(CompilerMode compilerMode)44   public DelegateBindingExpressionTest(CompilerMode compilerMode) {
45     this.compilerMode = compilerMode;
46   }
47 
48   private static final JavaFileObject REGULAR_SCOPED =
49       JavaFileObjects.forSourceLines(
50           "test.RegularScoped",
51           "package test;",
52           "",
53           "import javax.inject.Scope;",
54           "import javax.inject.Inject;",
55           "",
56           "@RegularScoped.CustomScope",
57           "class RegularScoped {",
58           "  @Inject RegularScoped() {}",
59           "",
60           "  @Scope @interface CustomScope {}",
61           "}");
62 
63   private static final JavaFileObject REUSABLE_SCOPED =
64       JavaFileObjects.forSourceLines(
65           "test.ReusableScoped",
66           "package test;",
67           "",
68           "import dagger.Reusable;",
69           "import javax.inject.Inject;",
70           "",
71           "@Reusable",
72           "class ReusableScoped {",
73           "  @Inject ReusableScoped() {}",
74           "}");
75 
76   private static final JavaFileObject UNSCOPED =
77       JavaFileObjects.forSourceLines(
78           "test.Unscoped",
79           "package test;",
80           "",
81           "import javax.inject.Inject;",
82           "",
83           "class Unscoped {",
84           "  @Inject Unscoped() {}",
85           "}");
86 
87   private static final JavaFileObject COMPONENT =
88       JavaFileObjects.forSourceLines(
89           "test.TestComponent",
90           "package test;",
91           "",
92           "import dagger.Component;",
93           "",
94           "@Component(modules = TestModule.class)",
95           "@RegularScoped.CustomScope",
96           "interface TestComponent {",
97           "  @Qualifier(RegularScoped.class)",
98           "  Object regular();",
99           "",
100           "  @Qualifier(ReusableScoped.class)",
101           "  Object reusable();",
102           "",
103           "  @Qualifier(Unscoped.class)",
104           "  Object unscoped();",
105           "}");
106 
107   private static final JavaFileObject QUALIFIER =
108       JavaFileObjects.forSourceLines(
109           "test.Qualifier",
110           "package test;",
111           "",
112           "@javax.inject.Qualifier",
113           "@interface Qualifier {",
114           "  Class<?> value();",
115           "}");
116 
117   @Test
toDoubleCheck()118   public void toDoubleCheck() {
119     JavaFileObject module =
120         JavaFileObjects.forSourceLines(
121             "test.TestModule",
122             "package test;",
123             "",
124             "import dagger.Binds;",
125             "import dagger.Module;",
126             "",
127             "@Module",
128             "interface TestModule {",
129             "  @Binds @RegularScoped.CustomScope @Qualifier(RegularScoped.class)",
130             "  Object regular(RegularScoped delegate);",
131             "",
132             "  @Binds @RegularScoped.CustomScope @Qualifier(ReusableScoped.class)",
133             "  Object reusable(ReusableScoped delegate);",
134             "",
135             "  @Binds @RegularScoped.CustomScope @Qualifier(Unscoped.class)",
136             "  Object unscoped(Unscoped delegate);",
137             "}");
138 
139     assertThatCompilationWithModule(module)
140         .generatedSourceFile("test.DaggerTestComponent")
141         .containsElementsIn(
142             compilerMode
143                 .javaFileBuilder("test.DaggerTestComponent")
144                 .addLines(
145                     "package test;",
146                     "",
147                     GENERATED_ANNOTATION,
148                     "final class DaggerTestComponent implements TestComponent {")
149                 .addLinesIn(
150                     FAST_INIT_MODE,
151                     "  private volatile Object regularScoped = new MemoizedSentinel();",
152                     "  private volatile ReusableScoped reusableScoped;",
153                     "",
154                     "  private RegularScoped getRegularScoped() {",
155                     "    Object local = regularScoped;",
156                     "    if (local instanceof MemoizedSentinel) {",
157                     "      synchronized (local) {",
158                     "        local = regularScoped;",
159                     "        if (local instanceof MemoizedSentinel) {",
160                     "          local = new RegularScoped();",
161                     "          regularScoped = DoubleCheck.reentrantCheck(regularScoped, local);",
162                     "        }",
163                     "      }",
164                     "    }",
165                     "    return (RegularScoped) local;",
166                     "  }",
167                     "",
168                     "  private ReusableScoped getReusableScoped() {",
169                     "    Object local = reusableScoped;",
170                     "    if (local == null) {",
171                     "      local = new ReusableScoped();",
172                     "      reusableScoped = (ReusableScoped) local;",
173                     "    }",
174                     "    return (ReusableScoped) local;",
175                     "  }",
176                     "")
177                 .addLinesIn(
178                     DEFAULT_MODE,
179                     "  @SuppressWarnings(\"unchecked\")",
180                     "  private void initialize() {",
181                     "    this.regularScopedProvider = ",
182                     "        DoubleCheck.provider(RegularScoped_Factory.create());",
183                     "    this.reusableScopedProvider = ",
184                     "        SingleCheck.provider(ReusableScoped_Factory.create());",
185                     "    this.reusableProvider = DoubleCheck.provider(",
186                     "        (Provider) reusableScopedProvider);",
187                     "    this.unscopedProvider = DoubleCheck.provider(",
188                     "        (Provider) Unscoped_Factory.create());",
189                     "  }")
190                 .addLines( //
191                     "}")
192                 .build());
193   }
194 
195   @Test
toSingleCheck()196   public void toSingleCheck() {
197     JavaFileObject module =
198         JavaFileObjects.forSourceLines(
199             "test.TestModule",
200             "package test;",
201             "",
202             "import dagger.Binds;",
203             "import dagger.Module;",
204             "import dagger.Reusable;",
205             "",
206             "@Module",
207             "interface TestModule {",
208             "  @Binds @Reusable @Qualifier(RegularScoped.class)",
209             "  Object regular(RegularScoped delegate);",
210             "",
211             "  @Binds @Reusable @Qualifier(ReusableScoped.class)",
212             "  Object reusable(ReusableScoped delegate);",
213             "",
214             "  @Binds @Reusable @Qualifier(Unscoped.class)",
215             "  Object unscoped(Unscoped delegate);",
216             "}");
217 
218     assertThatCompilationWithModule(module)
219         .generatedSourceFile("test.DaggerTestComponent")
220         .containsElementsIn(
221             compilerMode
222                 .javaFileBuilder("test.DaggerTestComponent")
223                 .addLines(
224                     "package test;",
225                     "",
226                     GENERATED_ANNOTATION,
227                     "final class DaggerTestComponent implements TestComponent {")
228                 .addLinesIn(
229                     FAST_INIT_MODE,
230                     "  private volatile Object regularScoped = new MemoizedSentinel();",
231                     "  private volatile ReusableScoped reusableScoped;",
232                     "",
233                     "  private RegularScoped getRegularScoped() {",
234                     "    Object local = regularScoped;",
235                     "    if (local instanceof MemoizedSentinel) {",
236                     "      synchronized (local) {",
237                     "        local = regularScoped;",
238                     "        if (local instanceof MemoizedSentinel) {",
239                     "          local = new RegularScoped();",
240                     "          regularScoped = DoubleCheck.reentrantCheck(regularScoped, local);",
241                     "        }",
242                     "      }",
243                     "    }",
244                     "    return (RegularScoped) local;",
245                     "  }",
246                     "",
247                     "  private ReusableScoped getReusableScoped() {",
248                     "    Object local = reusableScoped;",
249                     "    if (local == null) {",
250                     "      local = new ReusableScoped();",
251                     "      reusableScoped = (ReusableScoped) local;",
252                     "    }",
253                     "    return (ReusableScoped) local;",
254                     "  }",
255                     "")
256                 .addLinesIn(
257                     DEFAULT_MODE,
258                     "  @SuppressWarnings(\"unchecked\")",
259                     "  private void initialize() {",
260                     "    this.regularScopedProvider = ",
261                     "        DoubleCheck.provider(RegularScoped_Factory.create());",
262                     "    this.reusableScopedProvider = ",
263                     "        SingleCheck.provider(ReusableScoped_Factory.create());",
264                     "    this.unscopedProvider = SingleCheck.provider(",
265                     "        (Provider) Unscoped_Factory.create());",
266                     "  }")
267                 .addLines( //
268                     "}")
269                 .build());
270   }
271 
272   @Test
toUnscoped()273   public void toUnscoped() {
274     JavaFileObject module =
275         JavaFileObjects.forSourceLines(
276             "test.TestModule",
277             "package test;",
278             "",
279             "import dagger.Binds;",
280             "import dagger.Module;",
281             "",
282             "@Module",
283             "interface TestModule {",
284             "  @Binds @Qualifier(RegularScoped.class)",
285             "  Object regular(RegularScoped delegate);",
286             "",
287             "  @Binds @Qualifier(ReusableScoped.class)",
288             "  Object reusable(ReusableScoped delegate);",
289             "",
290             "  @Binds @Qualifier(Unscoped.class)",
291             "  Object unscoped(Unscoped delegate);",
292             "}");
293 
294     assertThatCompilationWithModule(module)
295         .generatedSourceFile("test.DaggerTestComponent")
296         .containsElementsIn(
297             compilerMode
298                 .javaFileBuilder("test.DaggerTestComponent")
299                 .addLines(
300                     "package test;",
301                     "",
302                     GENERATED_ANNOTATION,
303                     "final class DaggerTestComponent implements TestComponent {")
304                 .addLinesIn(
305                     FAST_INIT_MODE,
306                     "  private volatile Object regularScoped = new MemoizedSentinel();",
307                     "  private volatile ReusableScoped reusableScoped;",
308                     "",
309                     "  private RegularScoped getRegularScoped() {",
310                     "    Object local = regularScoped;",
311                     "    if (local instanceof MemoizedSentinel) {",
312                     "      synchronized (local) {",
313                     "        local = regularScoped;",
314                     "        if (local instanceof MemoizedSentinel) {",
315                     "          local = new RegularScoped();",
316                     "          regularScoped = DoubleCheck.reentrantCheck(regularScoped, local);",
317                     "        }",
318                     "      }",
319                     "    }",
320                     "    return (RegularScoped) local;",
321                     "  }",
322                     "",
323                     "  private ReusableScoped getReusableScoped() {",
324                     "    Object local = reusableScoped;",
325                     "    if (local == null) {",
326                     "      local = new ReusableScoped();",
327                     "      reusableScoped = (ReusableScoped) local;",
328                     "    }",
329                     "    return (ReusableScoped) local;",
330                     "  }",
331                     "")
332                 .addLinesIn(
333                     DEFAULT_MODE,
334                     "  @SuppressWarnings(\"unchecked\")",
335                     "  private void initialize() {",
336                     "    this.regularScopedProvider = ",
337                     "        DoubleCheck.provider(RegularScoped_Factory.create());",
338                     "    this.reusableScopedProvider = ",
339                     "        SingleCheck.provider(ReusableScoped_Factory.create());",
340                     "  }")
341                 .addLines( //
342                     "}")
343                 .build());
344   }
345 
346   @Test
castNeeded_rawTypes_Provider_get()347   public void castNeeded_rawTypes_Provider_get() {
348     JavaFileObject accessibleSupertype =
349         JavaFileObjects.forSourceLines(
350             "other.Supertype",
351             "package other;",
352             "",
353             // accessible from the component, but the subtype is not
354             "public interface Supertype {}");
355     JavaFileObject inaccessibleSubtype =
356         JavaFileObjects.forSourceLines(
357             "other.Subtype",
358             "package other;",
359             "",
360             "import javax.inject.Inject;",
361             "import javax.inject.Singleton;",
362             "",
363             "@Singleton",
364             "class Subtype implements Supertype {",
365             "  @Inject Subtype() {}",
366             "}");
367     JavaFileObject module =
368         JavaFileObjects.forSourceLines(
369             "other.SupertypeModule",
370             "package other;",
371             "",
372             "import dagger.Binds;",
373             "import dagger.Module;",
374             "",
375             "@Module",
376             "public interface SupertypeModule {",
377             "  @Binds Supertype to(Subtype subtype);",
378             "}");
379     JavaFileObject component =
380         JavaFileObjects.forSourceLines(
381             "test.TestComponent",
382             "package test;",
383             "",
384             "import dagger.Component;",
385             "import javax.inject.Singleton;",
386             "",
387             "@Singleton",
388             "@Component(modules = other.SupertypeModule.class)",
389             "interface TestComponent {",
390             "  other.Supertype supertype();",
391             "}");
392     Compilation compilation =
393         daggerCompiler()
394             .withOptions(compilerMode.javacopts())
395             .compile(accessibleSupertype, inaccessibleSubtype, module, component);
396     assertThat(compilation).succeeded();
397     assertThat(compilation)
398         .generatedSourceFile("test.DaggerTestComponent")
399         .containsElementsIn(
400             compilerMode
401                 .javaFileBuilder("test.DaggerTestComponent")
402                 .addLines(
403                     "package test;",
404                     "",
405                     GENERATED_ANNOTATION,
406                     "final class DaggerTestComponent implements TestComponent {")
407                 .addLinesIn(
408                     DEFAULT_MODE,
409                     "  @SuppressWarnings(\"rawtypes\")",
410                     "  private Provider subtypeProvider;",
411                     "",
412                     "  @SuppressWarnings(\"unchecked\")",
413                     "  private void initialize() {",
414                     "    this.subtypeProvider = DoubleCheck.provider(Subtype_Factory.create());",
415                     "  }",
416                     "",
417                     "  @Override",
418                     "  public Supertype supertype() {",
419                     "    return (Supertype) subtypeProvider.get();",
420                     "  }")
421                 .addLinesIn(
422                     FAST_INIT_MODE,
423                     "  private volatile Object subtype = new MemoizedSentinel();",
424                     "",
425                     "  private Object getSubtype() {",
426                     "    Object local = subtype;",
427                     "    if (local instanceof MemoizedSentinel) {",
428                     "      synchronized (local) {",
429                     "        local = subtype;",
430                     "        if (local instanceof MemoizedSentinel) {",
431                     "          local = Subtype_Factory.newInstance();",
432                     "          subtype = DoubleCheck.reentrantCheck(subtype, local);",
433                     "        }",
434                     "      }",
435                     "    }",
436                     "    return (Object) local;",
437                     "  }",
438                     "",
439                     "  @Override",
440                     "  public Supertype supertype() {",
441                     "    return (Supertype) getSubtype();",
442                     "  }")
443                 .build());
444   }
445 
446   @Test
noCast_rawTypes_Provider_get_toInaccessibleType()447   public void noCast_rawTypes_Provider_get_toInaccessibleType() {
448     JavaFileObject supertype =
449         JavaFileObjects.forSourceLines(
450             "other.Supertype",
451             "package other;",
452             "",
453             "interface Supertype {}");
454     JavaFileObject subtype =
455         JavaFileObjects.forSourceLines(
456             "other.Subtype",
457             "package other;",
458             "",
459             "import javax.inject.Inject;",
460             "import javax.inject.Singleton;",
461             "",
462             "@Singleton",
463             "class Subtype implements Supertype {",
464             "  @Inject Subtype() {}",
465             "}");
466     JavaFileObject usesSupertype =
467         JavaFileObjects.forSourceLines(
468             "other.UsesSupertype",
469             "package other;",
470             "",
471             "import javax.inject.Inject;",
472             "",
473             "public class UsesSupertype {",
474             "  @Inject UsesSupertype(Supertype supertype) {}",
475             "}");
476     JavaFileObject module =
477         JavaFileObjects.forSourceLines(
478             "other.SupertypeModule",
479             "package other;",
480             "",
481             "import dagger.Binds;",
482             "import dagger.Module;",
483             "",
484             "@Module",
485             "public interface SupertypeModule {",
486             "  @Binds Supertype to(Subtype subtype);",
487             "}");
488     JavaFileObject component =
489         JavaFileObjects.forSourceLines(
490             "test.TestComponent",
491             "package test;",
492             "",
493             "import dagger.Component;",
494             "import javax.inject.Singleton;",
495             "",
496             "@Singleton",
497             "@Component(modules = other.SupertypeModule.class)",
498             "interface TestComponent {",
499             "  other.UsesSupertype usesSupertype();",
500             "}");
501     Compilation compilation =
502         daggerCompiler()
503             .withOptions(compilerMode.javacopts())
504             .compile(supertype, subtype, usesSupertype, module, component);
505     assertThat(compilation).succeeded();
506     assertThat(compilation)
507         .generatedSourceFile("test.DaggerTestComponent")
508         .containsElementsIn(
509             compilerMode
510                 .javaFileBuilder("test.DaggerTestComponent")
511                 .addLines(
512                     "package test;",
513                     "",
514                     GENERATED_ANNOTATION,
515                     "final class DaggerTestComponent implements TestComponent {")
516                 .addLinesIn(
517                     DEFAULT_MODE,
518                     "  @SuppressWarnings(\"rawtypes\")",
519                     "  private Provider subtypeProvider;",
520                     "",
521                     "  @Override",
522                     "  public UsesSupertype usesSupertype() {",
523                     //   can't cast the provider.get() to a type that's not accessible
524                     "    return UsesSupertype_Factory.newInstance(subtypeProvider.get());",
525                     "  }",
526                     "}")
527                 .addLinesIn(
528                     FAST_INIT_MODE,
529                     "  private volatile Object subtype = new MemoizedSentinel();",
530                     "",
531                     "  private Object getSubtype() {",
532                     "    Object local = subtype;",
533                     "    if (local instanceof MemoizedSentinel) {",
534                     "      synchronized (local) {",
535                     "        local = subtype;",
536                     "        if (local instanceof MemoizedSentinel) {",
537                     "          local = Subtype_Factory.newInstance();",
538                     "          subtype = DoubleCheck.reentrantCheck(subtype, local);",
539                     "        }",
540                     "      }",
541                     "    }",
542                     "    return (Object) local;",
543                     "  }",
544                     "",
545                     "  @Override",
546                     "  public UsesSupertype usesSupertype() {",
547                     "    return UsesSupertype_Factory.newInstance(getSubtype());",
548                     "  }")
549                 .build());
550   }
551 
552   @Test
castedToRawType()553   public void castedToRawType() {
554     JavaFileObject module =
555         JavaFileObjects.forSourceLines(
556             "test.TestModule",
557             "package test;",
558             "",
559             "import dagger.Binds;",
560             "import dagger.Module;",
561             "import dagger.Provides;",
562             "import javax.inject.Named;",
563             "",
564             "@Module",
565             "interface TestModule {",
566             "  @Provides",
567             "  static String provideString() { return new String(); }",
568             "",
569             "  @Binds",
570             "  CharSequence charSequence(String string);",
571             "",
572             "  @Binds",
573             "  @Named(\"named\")",
574             "  String namedString(String string);",
575             "}");
576     JavaFileObject component =
577         JavaFileObjects.forSourceLines(
578             "test.TestComponent",
579             "package test;",
580             "",
581             "import dagger.Component;",
582             "import javax.inject.Named;",
583             "import javax.inject.Provider;",
584             "",
585             "@Component(modules = TestModule.class)",
586             "interface TestComponent {",
587             "  Provider<CharSequence> charSequence();",
588             "",
589             "  @Named(\"named\") Provider<String> namedString();",
590             "}");
591 
592     Compilation compilation =
593         daggerCompiler()
594             .withOptions(compilerMode.javacopts())
595             .compile(module, component);
596     assertThat(compilation).succeeded();
597     assertThat(compilation)
598         .generatedSourceFile("test.DaggerTestComponent")
599         .containsElementsIn(
600             compilerMode
601                 .javaFileBuilder("test.DaggerTestComponent")
602                 .addLines(
603                     "package test;",
604                     "",
605                     GENERATED_ANNOTATION,
606                     "final class DaggerTestComponent implements TestComponent {")
607                 .addLinesIn(
608                     DEFAULT_MODE,
609                     "  @Override",
610                     "  public Provider<CharSequence> charSequence() {",
611                     "    return (Provider) TestModule_ProvideStringFactory.create();",
612                     "  }",
613                     "",
614                     "  @Override",
615                     "  public Provider<String> namedString() {",
616                     "    return TestModule_ProvideStringFactory.create();",
617                     "  }",
618                     "}")
619                 .addLinesIn(
620                     FAST_INIT_MODE,
621                     "  private volatile Provider<String> provideStringProvider;",
622                     "",
623                     "  private Provider<String> getStringProvider() {",
624                     "    Object local = provideStringProvider;",
625                     "    if (local == null) {",
626                     "      local = new SwitchingProvider<>(0);",
627                     "      provideStringProvider = (Provider<String>) local;",
628                     "    }",
629                     "    return (Provider<String>) local;",
630                     "  }",
631                     "",
632                     "  @Override",
633                     "  public Provider<CharSequence> charSequence() {",
634                     "    return (Provider) getStringProvider();",
635                     "  }",
636                     "",
637                     "  @Override",
638                     "  public Provider<String> namedString() {",
639                     "    return getStringProvider();",
640                     "  }",
641                     "",
642                     "  private final class SwitchingProvider<T> implements Provider<T> {",
643                     "    @SuppressWarnings(\"unchecked\")",
644                     "    @Override",
645                     "    public T get() {",
646                     "      switch (id) {",
647                     "        case 0:",
648                     "            return (T) TestModule_ProvideStringFactory.provideString();",
649                     "        default:",
650                     "            throw new AssertionError(id);",
651                     "      }",
652                     "    }",
653                     "  }")
654                 .build());
655   }
656 
657   @Test
doubleBinds()658   public void doubleBinds() {
659     JavaFileObject module =
660         JavaFileObjects.forSourceLines(
661             "test.TestModule",
662             "package test;",
663             "",
664             "import dagger.Binds;",
665             "import dagger.Module;",
666             "import dagger.Provides;",
667             "",
668             "@Module",
669             "interface TestModule {",
670             "  @Provides",
671             "  static String provideString() { return new String(); }",
672             "",
673             "  @Binds",
674             "  CharSequence charSequence(String string);",
675             "",
676             "  @Binds",
677             "  Object object(CharSequence charSequence);",
678             "}");
679     JavaFileObject component =
680         JavaFileObjects.forSourceLines(
681             "test.TestComponent",
682             "package test;",
683             "",
684             "import dagger.Component;",
685             "import javax.inject.Named;",
686             "import javax.inject.Provider;",
687             "",
688             "@Component(modules = TestModule.class)",
689             "interface TestComponent {",
690             "  Provider<CharSequence> charSequence();",
691             "  Provider<Object> object();",
692             "}");
693 
694     Compilation compilation =
695         daggerCompiler()
696             .withOptions(compilerMode.javacopts())
697             .compile(module, component);
698     assertThat(compilation).succeeded();
699     assertThat(compilation)
700         .generatedSourceFile("test.DaggerTestComponent")
701         .containsElementsIn(
702             compilerMode
703                 .javaFileBuilder("test.DaggerTestComponent")
704                 .addLines(
705                     "package test;",
706                     "",
707                     GENERATED_ANNOTATION,
708                     "final class DaggerTestComponent implements TestComponent {")
709                 .addLinesIn(
710                     DEFAULT_MODE,
711                     "  @Override",
712                     "  public Provider<CharSequence> charSequence() {",
713                     "    return (Provider) TestModule_ProvideStringFactory.create();",
714                     "  }",
715                     "  @Override",
716                     "  public Provider<Object> object() {",
717                     "    return (Provider) TestModule_ProvideStringFactory.create();",
718                     "  }",
719                     "}")
720                 .addLinesIn(
721                     FAST_INIT_MODE,
722                     "  private volatile Provider<String> provideStringProvider;",
723                     "",
724                     "  private Provider<String> getStringProvider() {",
725                     "    Object local = provideStringProvider;",
726                     "    if (local == null) {",
727                     "      local = new SwitchingProvider<>(0);",
728                     "      provideStringProvider = (Provider<String>) local;",
729                     "    }",
730                     "    return (Provider<String>) local;",
731                     "  }",
732                     "",
733                     "  @Override",
734                     "  public Provider<CharSequence> charSequence() {",
735                     "    return (Provider) getStringProvider();",
736                     "  }",
737                     "",
738                     "  @Override",
739                     "  public Provider<Object> object() {",
740                     "    return (Provider) getStringProvider();",
741                     "  }",
742                     "",
743                     "  private final class SwitchingProvider<T> implements Provider<T> {",
744                     "    @SuppressWarnings(\"unchecked\")",
745                     "    @Override",
746                     "    public T get() {",
747                     "      switch (id) {",
748                     "        case 0:",
749                     "            return (T) TestModule_ProvideStringFactory.provideString();",
750                     "        default:",
751                     "            throw new AssertionError(id);",
752                     "      }",
753                     "    }",
754                     "  }")
755                 .build());
756   }
757 
758   @Test
inlineFactoryOfInacessibleType()759   public void inlineFactoryOfInacessibleType() {
760     JavaFileObject supertype =
761         JavaFileObjects.forSourceLines(
762             "other.Supertype", "package other;", "", "public interface Supertype {}");
763     JavaFileObject injectableSubtype =
764         JavaFileObjects.forSourceLines(
765             "other.Subtype",
766             "package other;",
767             "",
768             "import javax.inject.Inject;",
769             "",
770             "final class Subtype implements Supertype {",
771             // important: this doesn't have any dependencies and therefore the factory will be able
772             // to be referenced with an inline Subtype_Factory.create()
773             "  @Inject Subtype() {}",
774             "}");
775     JavaFileObject module =
776         JavaFileObjects.forSourceLines(
777             "other.TestModule",
778             "package other;",
779             "",
780             "import dagger.Binds;",
781             "import dagger.Module;",
782             "",
783             "@Module",
784             "public interface TestModule {",
785             "  @Binds Supertype to(Subtype subtype);",
786             "}");
787     JavaFileObject component =
788         JavaFileObjects.forSourceLines(
789             "test.RequestsSubtypeAsProvider",
790             "package test;",
791             "",
792             "import dagger.Component;",
793             "import javax.inject.Provider;",
794             "",
795             "@Component(modules = other.TestModule.class)",
796             "interface RequestsSubtypeAsProvider {",
797             "  Provider<other.Supertype> supertypeProvider();",
798             "}");
799 
800     Compilation compilation =
801         daggerCompiler()
802             .withOptions(compilerMode.javacopts())
803             .compile(supertype, injectableSubtype, module, component);
804     assertThat(compilation).succeeded();
805     assertThat(compilation)
806         .generatedSourceFile("test.DaggerRequestsSubtypeAsProvider")
807         .containsElementsIn(
808             compilerMode
809                 .javaFileBuilder("test.DaggerRequestsSubtypeAsProvider")
810                 .addLines(
811                     "package test;",
812                     "",
813                     GENERATED_ANNOTATION,
814                     "final class DaggerRequestsSubtypeAsProvider",
815                     "    implements RequestsSubtypeAsProvider {")
816                 .addLinesIn(
817                     DEFAULT_MODE,
818                     "  @Override",
819                     "  public Provider<Supertype> supertypeProvider() {",
820                     "    return (Provider) Subtype_Factory.create();",
821                     "  }",
822                     "}")
823                 .addLinesIn(
824                     FAST_INIT_MODE,
825                     "  private volatile Provider subtypeProvider;",
826                     "",
827                     "  private Provider getSubtypeProvider() {",
828                     "    Object local = subtypeProvider;",
829                     "    if (local == null) {",
830                     "      local = new SwitchingProvider<>(0);",
831                     "      subtypeProvider = (Provider) local;",
832                     "    }",
833                     "    return (Provider) local;",
834                     "  }",
835                     "",
836                     "  @Override",
837                     "  public Provider<Supertype> supertypeProvider() {",
838                     "    return getSubtypeProvider();",
839                     "  }",
840                     "",
841                     "  private final class SwitchingProvider<T> implements Provider<T> {",
842                     "    @SuppressWarnings(\"unchecked\")",
843                     "    @Override",
844                     "    public T get() {",
845                     "      switch (id) {",
846                     "        case 0: return (T) Subtype_Factory.newInstance();",
847                     "        default: throw new AssertionError(id);",
848                     "      }",
849                     "    }",
850                     "  }")
851                 .build());
852   }
853 
854   @Test
providerWhenBindsScopeGreaterThanDependencyScope()855   public void providerWhenBindsScopeGreaterThanDependencyScope() {
856     JavaFileObject module =
857         JavaFileObjects.forSourceLines(
858             "test.TestModule",
859             "package test;",
860             "",
861             "import dagger.Binds;",
862             "import dagger.Module;",
863             "import dagger.Provides;",
864             "import dagger.Reusable;",
865             "import javax.inject.Singleton;",
866             "",
867             "@Module",
868             "public abstract class TestModule {",
869             "  @Reusable",
870             "  @Provides",
871             "  static String provideString() {",
872             "    return \"\";",
873             "  }",
874             "",
875             "  @Binds",
876             "  @Singleton",
877             "  abstract Object bindString(String str);",
878             "}");
879     JavaFileObject component =
880         JavaFileObjects.forSourceLines(
881             "test.TestComponent",
882             "package test;",
883             "",
884             "import dagger.Component;",
885             "import javax.inject.Singleton;",
886             "import javax.inject.Provider;",
887             "",
888             "@Singleton",
889             "@Component(modules = TestModule.class)",
890             "interface TestComponent {",
891             "  Provider<Object> getObject();",
892             "}");
893 
894     Compilation compilation = daggerCompiler()
895         .withOptions(compilerMode.javacopts())
896         .compile(module, component);
897     assertThat(compilation).succeeded();
898     assertThat(compilation)
899         .generatedSourceFile("test.DaggerTestComponent")
900         .containsElementsIn(
901             compilerMode
902                 .javaFileBuilder("test.DaggerTestComponent")
903                 .addLines(
904                     "package test;",
905                     "",
906                     GENERATED_ANNOTATION,
907                     "final class DaggerTestComponent implements TestComponent {")
908                 .addLinesIn(
909                     DEFAULT_MODE,
910                     "  private Provider<String> provideStringProvider;",
911                     "  private Provider<Object> bindStringProvider;",
912                     "",
913                     "  @SuppressWarnings(\"unchecked\")",
914                     "  private void initialize() {",
915                     "    this.provideStringProvider =",
916                     "        SingleCheck.provider(TestModule_ProvideStringFactory.create());",
917                     "    this.bindStringProvider =",
918                     "        DoubleCheck.provider((Provider) provideStringProvider);",
919                     "  }",
920                     "",
921                     "  @Override",
922                     "  public Provider<Object> getObject() {",
923                     "    return bindStringProvider;",
924                     "  }",
925                     "}")
926                 .addLinesIn(
927                     FAST_INIT_MODE,
928                     "  private volatile String string;",
929                     "  private volatile Object object = new MemoizedSentinel();",
930                     "  private volatile Provider<Object> bindStringProvider;",
931                     "",
932                     "  private String getString() {",
933                     "    Object local = string;",
934                     "    if (local == null) {",
935                     "      local = TestModule_ProvideStringFactory.provideString();",
936                     "      string = (String) local;",
937                     "    }",
938                     "    return (String) local;",
939                     "  }",
940                     "",
941                     "  private Object getObject2() {",
942                     "    Object local = object;",
943                     "    if (local instanceof MemoizedSentinel) {",
944                     "      synchronized (local) {",
945                     "        local = object;",
946                     "        if (local instanceof MemoizedSentinel) {",
947                     "          local = getString();",
948                     "          object = DoubleCheck.reentrantCheck(object, local);",
949                     "        }",
950                     "      }",
951                     "    }",
952                     "    return (Object) local;",
953                     "  }",
954                     "",
955                     "  @Override",
956                     "  public Provider<Object> getObject() {",
957                     "    Object local = bindStringProvider;",
958                     "    if (local == null) {",
959                     "      local = new SwitchingProvider<>(0);",
960                     "      bindStringProvider = (Provider<Object>) local;",
961                     "    }",
962                     "    return (Provider<Object>) local;",
963                     "  }",
964                     "",
965                     "  private final class SwitchingProvider<T> implements Provider<T> {",
966                     "    @SuppressWarnings(\"unchecked\")",
967                     "    @Override",
968                     "    public T get() {",
969                     "      switch (id) {",
970                     "        case 0: return (T) DaggerTestComponent.this.getObject2();",
971                     "        default: throw new AssertionError(id);",
972                     "      }",
973                     "    }",
974                     "  }")
975                 .build());
976   }
977 
assertThatCompilationWithModule(JavaFileObject module)978   private CompilationSubject assertThatCompilationWithModule(JavaFileObject module) {
979     Compilation compilation =
980         daggerCompiler()
981             .withOptions(compilerMode.javacopts())
982             .compile(
983                 module,
984                 COMPONENT,
985                 QUALIFIER,
986                 REGULAR_SCOPED,
987                 REUSABLE_SCOPED,
988                 UNSCOPED);
989     assertThat(compilation).succeeded();
990     return assertThat(compilation);
991   }
992 }
993