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