• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen;
18 
19 import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatMethodInUnannotatedClass;
20 import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod;
21 
22 import androidx.room.compiler.processing.XProcessingEnv;
23 import androidx.room.compiler.processing.util.Source;
24 import com.google.common.collect.ImmutableList;
25 import com.google.common.collect.ImmutableMap;
26 import dagger.testing.compile.CompilerTests;
27 import dagger.testing.golden.GoldenFileRule;
28 import org.junit.Rule;
29 import org.junit.Test;
30 import org.junit.runner.RunWith;
31 import org.junit.runners.JUnit4;
32 
33 @RunWith(JUnit4.class)
34 public class ModuleFactoryGeneratorTest {
35 
36   private static final Source NON_TYPE_USE_NULLABLE =
37       CompilerTests.javaSource(
38           "test.Nullable", // force one-string-per-line format
39           "package test;",
40           "",
41           "public @interface Nullable {}");
42 
43   @Rule public GoldenFileRule goldenFileRule = new GoldenFileRule();
44 
45   // TODO(gak): add tests for invalid combinations of scope and qualifier annotations like we have
46   // for @Inject
47 
providesMethodNotInModule()48   @Test public void providesMethodNotInModule() {
49     assertThatMethodInUnannotatedClass("@Provides String provideString() { return null; }")
50         .hasError("@Provides methods can only be present within a @Module or @ProducerModule");
51   }
52 
providesMethodAbstract()53   @Test public void providesMethodAbstract() {
54     assertThatModuleMethod("@Provides abstract String abstractMethod();")
55         .hasError("@Provides methods cannot be abstract");
56   }
57 
providesMethodPrivate()58   @Test public void providesMethodPrivate() {
59     assertThatModuleMethod("@Provides private String privateMethod() { return null; }")
60         .hasError("@Provides methods cannot be private");
61   }
62 
providesMethodReturnVoid()63   @Test public void providesMethodReturnVoid() {
64     assertThatModuleMethod("@Provides void voidMethod() {}")
65         .hasError("@Provides methods must return a value (not void)");
66   }
67 
68   @Test
providesMethodReturnsProvider()69   public void providesMethodReturnsProvider() {
70     assertThatModuleMethod("@Provides Provider<String> provideProvider() {}")
71         .hasError("@Provides methods must not return framework types");
72   }
73 
74   @Test
providesMethodReturnsJakartaProvider()75   public void providesMethodReturnsJakartaProvider() {
76     assertThatModuleMethod("@Provides jakarta.inject.Provider<String> provideProvider() {}")
77         .hasError("@Provides methods must not return framework types");
78   }
79 
80   @Test
providesMethodReturnsDaggerInternalProvider()81   public void providesMethodReturnsDaggerInternalProvider() {
82     assertThatModuleMethod("@Provides dagger.internal.Provider<String> provideProvider() {}")
83         .hasError("@Provides methods must not return disallowed types");
84   }
85 
86   @Test
providesIntoSetMethodReturnsDaggerInternalProvider()87   public void providesIntoSetMethodReturnsDaggerInternalProvider() {
88     assertThatModuleMethod(
89         "@Provides @IntoSet dagger.internal.Provider<String> provideProvider() {}")
90         .hasError("@Provides methods must not return disallowed types");
91   }
92 
93   @Test
providesMethodReturnsLazy()94   public void providesMethodReturnsLazy() {
95     assertThatModuleMethod("@Provides Lazy<String> provideLazy() {}")
96         .hasError("@Provides methods must not return framework types");
97   }
98 
99   @Test
providesMethodReturnsMembersInjector()100   public void providesMethodReturnsMembersInjector() {
101     assertThatModuleMethod("@Provides MembersInjector<String> provideMembersInjector() {}")
102         .hasError("@Provides methods must not return framework types");
103   }
104 
105   @Test
providesMethodReturnsProducer()106   public void providesMethodReturnsProducer() {
107     assertThatModuleMethod("@Provides Producer<String> provideProducer() {}")
108         .hasError("@Provides methods must not return framework types");
109   }
110 
111   @Test
providesMethodReturnsProduced()112   public void providesMethodReturnsProduced() {
113     assertThatModuleMethod("@Provides Produced<String> provideProduced() {}")
114         .hasError("@Provides methods must not return framework types");
115   }
116 
providesMethodWithTypeParameter()117   @Test public void providesMethodWithTypeParameter() {
118     assertThatModuleMethod("@Provides <T> String typeParameter() { return null; }")
119         .hasError("@Provides methods may not have type parameters");
120   }
121 
providesMethodSetValuesWildcard()122   @Test public void providesMethodSetValuesWildcard() {
123     assertThatModuleMethod("@Provides @ElementsIntoSet Set<?> provideWildcard() { return null; }")
124         .hasError(
125             "@Provides methods must return a primitive, an array, a type variable, "
126                 + "or a declared type");
127   }
128 
providesMethodSetValuesRawSet()129   @Test public void providesMethodSetValuesRawSet() {
130     assertThatModuleMethod("@Provides @ElementsIntoSet Set provideSomething() { return null; }")
131         .hasError("@Provides methods annotated with @ElementsIntoSet cannot return a raw Set");
132   }
133 
providesElementsIntoSetMethodReturnsSetDaggerProvider()134   @Test public void providesElementsIntoSetMethodReturnsSetDaggerProvider() {
135     assertThatModuleMethod(
136         "@Provides @ElementsIntoSet Set<dagger.internal.Provider<String>> provideProvider() {}")
137         .hasError("@Provides methods must not return disallowed types");
138   }
139 
providesMethodSetValuesNotASet()140   @Test public void providesMethodSetValuesNotASet() {
141     assertThatModuleMethod(
142             "@Provides @ElementsIntoSet List<String> provideStrings() { return null; }")
143         .hasError("@Provides methods annotated with @ElementsIntoSet must return a Set");
144   }
145 
146   @Test
bindsMethodReturnsProvider()147   public void bindsMethodReturnsProvider() {
148     assertThatModuleMethod("@Binds abstract Provider<Number> bindsProvider(Provider<Long> impl);")
149         .hasError("@Binds methods must not return framework types");
150   }
151 
152   @Test
bindsMethodReturnsDaggerProvider()153   public void bindsMethodReturnsDaggerProvider() {
154     assertThatModuleMethod("@Binds abstract dagger.internal.Provider<Number> "
155         + "bindsProvider(dagger.internal.Provider<Long> impl);")
156         .hasError("@Binds methods must not return disallowed types");
157   }
158 
modulesWithTypeParamsMustBeAbstract()159   @Test public void modulesWithTypeParamsMustBeAbstract() {
160     Source moduleFile =
161         CompilerTests.javaSource(
162             "test.TestModule",
163             "package test;",
164             "",
165             "import dagger.Module;",
166             "",
167             "@Module",
168             "final class TestModule<A> {}");
169     CompilerTests.daggerCompiler(moduleFile)
170         .compile(
171             subject -> {
172               subject.hasErrorCount(1);
173               subject.hasErrorContaining("Modules with type parameters must be abstract")
174                   .onSource(moduleFile)
175                   .onLine(6);
176             });
177   }
178 
provideOverriddenByNoProvide()179   @Test public void provideOverriddenByNoProvide() {
180     Source parent =
181         CompilerTests.javaSource(
182             "test.Parent",
183             "package test;",
184             "",
185             "import dagger.Module;",
186             "import dagger.Provides;",
187             "",
188             "@Module",
189             "class Parent {",
190             "  @Provides String foo() { return null; }",
191             "}");
192     assertThatModuleMethod("String foo() { return null; }")
193         .withDeclaration("@Module class %s extends Parent { %s }")
194         .withAdditionalSources(parent)
195         .hasError(
196             "Binding methods may not be overridden in modules. Overrides: "
197                 + "@Provides String test.Parent.foo()");
198   }
199 
provideOverriddenByProvide()200   @Test public void provideOverriddenByProvide() {
201     Source parent =
202         CompilerTests.javaSource(
203             "test.Parent",
204             "package test;",
205             "",
206             "import dagger.Module;",
207             "import dagger.Provides;",
208             "",
209             "@Module",
210             "class Parent {",
211             "  @Provides String foo() { return null; }",
212             "}");
213     assertThatModuleMethod("@Provides String foo() { return null; }")
214         .withDeclaration("@Module class %s extends Parent { %s }")
215         .withAdditionalSources(parent)
216         .hasError(
217             "Binding methods may not override another method. Overrides: "
218                 + "@Provides String test.Parent.foo()");
219   }
220 
providesOverridesNonProvides()221   @Test public void providesOverridesNonProvides() {
222     Source parent =
223         CompilerTests.javaSource(
224             "test.Parent",
225             "package test;",
226             "",
227             "import dagger.Module;",
228             "",
229             "@Module",
230             "class Parent {",
231             "  String foo() { return null; }",
232             "}");
233     assertThatModuleMethod("@Provides String foo() { return null; }")
234         .withDeclaration("@Module class %s extends Parent { %s }")
235         .withAdditionalSources(parent)
236         .hasError(
237             "Binding methods may not override another method. Overrides: "
238                 + "String test.Parent.foo()");
239   }
240 
validatesIncludedModules()241   @Test public void validatesIncludedModules() {
242     Source module =
243         CompilerTests.javaSource(
244             "test.Parent",
245             "package test;",
246             "",
247             "import dagger.Module;",
248             "",
249             "@Module(",
250             "    includes = {",
251             "        Void.class,",
252             "        String.class,",
253             "    }",
254             ")",
255             "class TestModule {}");
256 
257     CompilerTests.daggerCompiler(module)
258         .compile(
259             subject -> {
260               subject.hasErrorCount(2);
261               // We avoid asserting on the line number because ksp and javac report different lines.
262               // The main issue here is that ksp doesn't allow reporting errors on individual
263               // annotation values, it only allows reporting errors on annotations themselves.
264               subject.hasErrorContaining(
265                       "java.lang.Void is listed as a module, but is not annotated with @Module")
266                   .onSource(module);
267               subject.hasErrorContaining(
268                       "java.lang.String is listed as a module, but is not annotated with @Module")
269                   .onSource(module);
270             });
271   }
272 
singleProvidesMethodNoArgs()273   @Test public void singleProvidesMethodNoArgs() {
274     Source moduleFile =
275         CompilerTests.javaSource(
276             "test.TestModule",
277             "package test;",
278             "",
279             "import dagger.Module;",
280             "import dagger.Provides;",
281             "",
282             "@Module",
283             "final class TestModule {",
284             "  @Provides String provideString() {",
285             "    return \"\";",
286             "  }",
287             "}");
288     CompilerTests.daggerCompiler(moduleFile)
289         .compile(
290             subject -> {
291               subject.hasErrorCount(0);
292               subject.generatedSource(
293                   goldenFileRule.goldenSource("test/TestModule_ProvideStringFactory"));
294             });
295   }
296 
singleProvidesMethodNoArgs_disableNullable()297   @Test public void singleProvidesMethodNoArgs_disableNullable() {
298     Source moduleFile =
299         CompilerTests.javaSource(
300             "test.TestModule",
301             "package test;",
302             "",
303             "import dagger.Module;",
304             "import dagger.Provides;",
305             "",
306             "@Module",
307             "final class TestModule {",
308             "  @Provides String provideString() {",
309             "    return \"\";",
310             "  }",
311             "}");
312     CompilerTests.daggerCompiler(moduleFile)
313         .withProcessingOptions(ImmutableMap.of("dagger.nullableValidation", "WARNING"))
314         .compile(
315             subject -> {
316               subject.hasErrorCount(0);
317               subject.generatedSource(
318                   goldenFileRule.goldenSource("test/TestModule_ProvideStringFactory"));
319             });
320   }
321 
322   @Test
nonTypeUseNullableProvides()323   public void nonTypeUseNullableProvides() {
324     Source moduleFile =
325         CompilerTests.javaSource(
326             "test.TestModule",
327             "package test;",
328             "",
329             "import dagger.Module;",
330             "import dagger.Provides;",
331             "",
332             "@Module",
333             "final class TestModule {",
334             "  @Provides @Nullable String provideString() { return null; }",
335             "}");
336     CompilerTests.daggerCompiler(moduleFile, NON_TYPE_USE_NULLABLE)
337         .compile(
338             subject -> {
339               subject.hasErrorCount(0);
340               subject.generatedSource(
341                   goldenFileRule.goldenSource("test/TestModule_ProvideStringFactory"));
342             });
343   }
344 
345   @Test
kotlinNullableProvides()346   public void kotlinNullableProvides() {
347     Source moduleFile =
348         CompilerTests.kotlinSource(
349             "TestModule.kt",
350             "package test",
351             "",
352             "import dagger.Module;",
353             "import dagger.Provides;",
354             "",
355             "@Module",
356             "class TestModule {",
357             "  @Provides fun provideString(): String? { return null; }",
358             "}");
359     CompilerTests.daggerCompiler(moduleFile)
360         .compile(
361             subject -> {
362               subject.hasErrorCount(0);
363               boolean isJavac = CompilerTests.backend(subject) == XProcessingEnv.Backend.JAVAC;
364               subject.generatedSource(
365                   CompilerTests.javaSource(
366                       "test.TestModule_ProvideStringFactory",
367                       "package test;",
368                       "",
369                       "import dagger.internal.DaggerGenerated;",
370                       "import dagger.internal.Factory;",
371                       "import dagger.internal.QualifierMetadata;",
372                       "import dagger.internal.ScopeMetadata;",
373                       "import javax.annotation.processing.Generated;",
374                       isJavac ? "import org.jetbrains.annotations.Nullable;\n" : "",
375                       "@ScopeMetadata",
376                       "@QualifierMetadata",
377                       "@DaggerGenerated",
378                       "@Generated(",
379                       "    value = \"dagger.internal.codegen.ComponentProcessor\",",
380                       "    comments = \"https://dagger.dev\"",
381                       ")",
382                       "@SuppressWarnings({",
383                       "    \"unchecked\",",
384                       "    \"rawtypes\",",
385                       "    \"KotlinInternal\",",
386                       "    \"KotlinInternalInJava\",",
387                       "    \"cast\",",
388                       "    \"deprecation\",",
389                       "    \"nullness:initialization.field.uninitialized\"",
390                       "})",
391                       "public final class TestModule_ProvideStringFactory implements"
392                           + " Factory<String> {",
393                       "  private final TestModule module;",
394                       "",
395                       "  public TestModule_ProvideStringFactory(TestModule module) {",
396                       "    this.module = module;",
397                       "  }",
398                       "",
399                       // TODO(b/368129744): KSP should output the @Nullable annotation after this
400                       // bug is fixed.
401                       isJavac ? "  @Override\n  @Nullable" : "  @Override",
402                       "  public String get() {",
403                       "    return provideString(module);",
404                       "  }",
405                       "",
406                       "  public static TestModule_ProvideStringFactory create(TestModule module) {",
407                       "    return new TestModule_ProvideStringFactory(module);",
408                       "  }",
409                       // TODO(b/368129744): KSP should output the @Nullable annotation after this
410                       // bug is fixed.
411                       isJavac ? "\n  @Nullable" : "",
412                       "  public static String provideString(TestModule instance) {",
413                       "    return instance.provideString();",
414                       "  }",
415                       "}"));
416             });
417   }
418 
multipleProvidesMethods()419   @Test public void multipleProvidesMethods() {
420     Source classXFile =
421         CompilerTests.javaSource("test.X",
422         "package test;",
423         "",
424         "import javax.inject.Inject;",
425         "",
426         "class X {",
427         "  @Inject public String s;",
428         "}");
429     Source moduleFile =
430         CompilerTests.javaSource(
431             "test.TestModule",
432             "package test;",
433             "",
434             "import dagger.MembersInjector;",
435             "import dagger.Module;",
436             "import dagger.Provides;",
437             "import java.util.Arrays;",
438             "import java.util.List;",
439             "",
440             "@Module",
441             "final class TestModule {",
442             "  @Provides List<Object> provideObjects(",
443             "      @QualifierA Object a, @QualifierB Object b, MembersInjector<X> xInjector) {",
444             "    return Arrays.asList(a, b);",
445             "  }",
446             "",
447             "  @Provides @QualifierA Object provideAObject() {",
448             "    return new Object();",
449             "  }",
450             "",
451             "  @Provides @QualifierB Object provideBObject() {",
452             "    return new Object();",
453             "  }",
454             "}");
455     CompilerTests.daggerCompiler(classXFile, moduleFile, QUALIFIER_A, QUALIFIER_B)
456         .compile(
457             subject -> {
458               subject.hasErrorCount(0);
459               subject.generatedSource(
460                   goldenFileRule.goldenSource("test/TestModule_ProvideObjectsFactory"));
461             });
462   }
463 
464   @Test
providesSetElement()465   public void providesSetElement() {
466     Source moduleFile =
467         CompilerTests.javaSource(
468             "test.TestModule",
469             "package test;",
470             "",
471             "import java.util.logging.Logger;",
472             "import dagger.Module;",
473             "import dagger.Provides;",
474             "import dagger.multibindings.IntoSet;",
475             "",
476             "@Module",
477             "final class TestModule {",
478             "  @Provides @IntoSet String provideString() {",
479             "    return \"\";",
480             "  }",
481             "}");
482     CompilerTests.daggerCompiler(moduleFile)
483         .compile(
484             subject -> {
485               subject.hasErrorCount(0);
486               subject.generatedSource(
487                   goldenFileRule.goldenSource("test/TestModule_ProvideStringFactory"));
488             });
489   }
490 
providesSetElementWildcard()491   @Test public void providesSetElementWildcard() {
492     Source moduleFile =
493         CompilerTests.javaSource(
494             "test.TestModule",
495             "package test;",
496             "",
497             "import java.util.logging.Logger;",
498             "import dagger.Module;",
499             "import dagger.Provides;",
500             "import dagger.multibindings.IntoSet;",
501             "import java.util.ArrayList;",
502             "import java.util.List;",
503             "",
504             "@Module",
505             "final class TestModule {",
506             "  @Provides @IntoSet List<List<?>> provideWildcardList() {",
507             "    return new ArrayList<>();",
508             "  }",
509             "}");
510     CompilerTests.daggerCompiler(moduleFile)
511         .compile(
512             subject -> {
513               subject.hasErrorCount(0);
514               subject.generatedSource(
515                   goldenFileRule.goldenSource("test/TestModule_ProvideWildcardListFactory"));
516             });
517   }
518 
providesSetValues()519   @Test public void providesSetValues() {
520     Source moduleFile =
521         CompilerTests.javaSource(
522             "test.TestModule",
523             "package test;",
524             "",
525             "import dagger.Module;",
526             "import dagger.Provides;",
527             "import dagger.multibindings.ElementsIntoSet;",
528             "import java.util.Set;",
529             "",
530             "@Module",
531             "final class TestModule {",
532             "  @Provides @ElementsIntoSet Set<String> provideStrings() {",
533             "    return null;",
534             "  }",
535             "}");
536     CompilerTests.daggerCompiler(moduleFile)
537         .compile(
538             subject -> {
539               subject.hasErrorCount(0);
540               subject.generatedSource(
541                   goldenFileRule.goldenSource("test/TestModule_ProvideStringsFactory"));
542             });
543   }
544 
multipleProvidesMethodsWithSameName()545   @Test public void multipleProvidesMethodsWithSameName() {
546     Source moduleFile =
547         CompilerTests.javaSource("test.TestModule",
548         "package test;",
549         "",
550         "import dagger.Module;",
551         "import dagger.Provides;",
552         "",
553         "@Module",
554         "final class TestModule {",
555         "  @Provides Object provide(int i) {",
556         "    return i;",
557         "  }",
558         "",
559         "  @Provides String provide() {",
560         "    return \"\";",
561         "  }",
562         "}");
563     CompilerTests.daggerCompiler(moduleFile)
564         .compile(
565             subject -> {
566               subject.hasErrorCount(2);
567               subject.hasErrorContaining(
568                       "Cannot have more than one binding method with the same name in a single "
569                           + "module")
570                   .onSource(moduleFile)
571                   .onLine(8);
572               subject.hasErrorContaining(
573                       "Cannot have more than one binding method with the same name in a single "
574                           + "module")
575                   .onSource(moduleFile)
576                   .onLine(12);
577             });
578   }
579 
580   @Test
providesMethodThrowsChecked()581   public void providesMethodThrowsChecked() {
582     Source moduleFile =
583         CompilerTests.javaSource(
584             "test.TestModule",
585             "package test;",
586             "",
587             "import dagger.Module;",
588             "import dagger.Provides;",
589             "",
590             "@Module",
591             "final class TestModule {",
592             "  @Provides int i() throws Exception {",
593             "    return 0;",
594             "  }",
595             "",
596             "  @Provides String s() throws Throwable {",
597             "    return \"\";",
598             "  }",
599             "}");
600     CompilerTests.daggerCompiler(moduleFile)
601         .compile(
602             subject -> {
603               subject.hasErrorCount(2);
604               subject.hasErrorContaining("@Provides methods may only throw unchecked exceptions")
605                   .onSource(moduleFile)
606                   .onLine(8);
607               subject.hasErrorContaining("@Provides methods may only throw unchecked exceptions")
608                   .onSource(moduleFile)
609                   .onLine(12);
610             });
611   }
612 
613   @Test
providedTypes()614   public void providedTypes() {
615     Source moduleFile =
616         CompilerTests.javaSource(
617             "test.TestModule",
618             "package test;",
619             "",
620             "import dagger.Module;",
621             "import dagger.Provides;",
622             "import java.io.Closeable;",
623             "import java.util.Set;",
624             "",
625             "@Module",
626             "final class TestModule {",
627             "  @Provides String string() {",
628             "    return null;",
629             "  }",
630             "",
631             "  @Provides Set<String> strings() {",
632             "    return null;",
633             "  }",
634             "",
635             "  @Provides Set<? extends Closeable> closeables() {",
636             "    return null;",
637             "  }",
638             "",
639             "  @Provides String[] stringArray() {",
640             "    return null;",
641             "  }",
642             "",
643             "  @Provides int integer() {",
644             "    return 0;",
645             "  }",
646             "",
647             "  @Provides int[] integers() {",
648             "    return null;",
649             "  }",
650             "}");
651     CompilerTests.daggerCompiler(moduleFile).compile(subject -> subject.hasErrorCount(0));
652   }
653 
654   @Test
privateModule()655   public void privateModule() {
656     Source moduleFile =
657         CompilerTests.javaSource(
658             "test.Enclosing",
659             "package test;",
660             "",
661             "import dagger.Module;",
662             "",
663             "final class Enclosing {",
664             "  @Module private static final class PrivateModule {",
665             "  }",
666             "}");
667     CompilerTests.daggerCompiler(moduleFile)
668         .compile(
669             subject -> {
670               subject.hasErrorCount(1);
671               subject.hasErrorContaining("Modules cannot be private")
672                   .onSource(moduleFile)
673                   .onLine(6);
674             });
675   }
676 
677   @Test
privateModule_kotlin()678   public void privateModule_kotlin() {
679     Source moduleFile =
680         CompilerTests.kotlinSource(
681             "test.TestModule.kt",
682             "package test",
683             "",
684             "import dagger.Component",
685             "import dagger.Module",
686             "import dagger.Provides",
687             "",
688             "@Module",
689             "private class TestModule {",
690             "  @Provides fun provideInt(): Int = 1",
691             "}");
692 
693     CompilerTests.daggerCompiler(moduleFile)
694         .compile(
695             subject -> {
696               subject.hasErrorCount(1);
697               subject
698                   .hasErrorContaining("Modules cannot be private")
699                   .onSource(moduleFile);
700             });
701   }
702 
703   @Test
enclosedInPrivateModule()704   public void enclosedInPrivateModule() {
705     Source moduleFile =
706         CompilerTests.javaSource("test.Enclosing",
707         "package test;",
708         "",
709         "import dagger.Module;",
710         "",
711         "final class Enclosing {",
712         "  private static final class PrivateEnclosing {",
713         "    @Module static final class TestModule {",
714         "    }",
715         "  }",
716         "}");
717     CompilerTests.daggerCompiler(moduleFile)
718         .compile(
719             subject -> {
720               subject.hasErrorCount(1);
721               subject.hasErrorContaining("Modules cannot be enclosed in private types")
722                   .onSource(moduleFile)
723                   .onLine(7);
724             });
725   }
726 
727   @Test
publicModuleNonPublicIncludes()728   public void publicModuleNonPublicIncludes() {
729     Source publicModuleFile =
730         CompilerTests.javaSource("test.PublicModule",
731         "package test;",
732         "",
733         "import dagger.Module;",
734         "",
735         "@Module(includes = {",
736         "    BadNonPublicModule.class, OtherPublicModule.class, OkNonPublicModule.class",
737         "})",
738         "public final class PublicModule {",
739         "}");
740     Source badNonPublicModuleFile =
741         CompilerTests.javaSource(
742             "test.BadNonPublicModule",
743             "package test;",
744             "",
745             "import dagger.Module;",
746             "import dagger.Provides;",
747             "",
748             "@Module",
749             "final class BadNonPublicModule {",
750             "  @Provides",
751             "  int provideInt() {",
752             "    return 42;",
753             "  }",
754             "}");
755     Source okNonPublicModuleFile =
756         CompilerTests.javaSource("test.OkNonPublicModule",
757         "package test;",
758         "",
759         "import dagger.Module;",
760         "import dagger.Provides;",
761         "",
762         "@Module",
763         "final class OkNonPublicModule {",
764         "  @Provides",
765         "  static String provideString() {",
766         "    return \"foo\";",
767         "  }",
768         "}");
769     Source otherPublicModuleFile =
770         CompilerTests.javaSource("test.OtherPublicModule",
771         "package test;",
772         "",
773         "import dagger.Module;",
774         "",
775         "@Module",
776         "public final class OtherPublicModule {",
777         "}");
778     CompilerTests.daggerCompiler(
779             publicModuleFile,
780             badNonPublicModuleFile,
781             okNonPublicModuleFile,
782             otherPublicModuleFile)
783         .compile(
784             subject -> {
785               subject.hasErrorCount(1);
786               subject.hasErrorContaining(
787                       "This module is public, but it includes non-public (or effectively non-"
788                           + "public) modules (test.BadNonPublicModule) that have non-static, non-"
789                           + "abstract binding methods. Either reduce the visibility of this module"
790                           + ", make the included modules public, or make all of the binding "
791                           + "methods on the included modules abstract or static.")
792                   .onSource(publicModuleFile)
793                   .onLine(8);
794             });
795   }
796 
797   @Test
genericSubclassedModule()798   public void genericSubclassedModule() {
799     Source parent =
800         CompilerTests.javaSource(
801             "test.ParentModule",
802             "package test;",
803             "",
804             "import dagger.Module;",
805             "import dagger.Provides;",
806             "import dagger.multibindings.IntoSet;",
807             "import dagger.multibindings.IntoMap;",
808             "import dagger.multibindings.StringKey;",
809             "import java.util.List;",
810             "import java.util.ArrayList;",
811             "",
812             "@Module",
813             "abstract class ParentModule<A extends CharSequence,",
814             "                            B,",
815             "                            C extends Number & Comparable<C>> {",
816             "  @Provides List<B> provideListB(B b) {",
817             "    List<B> list = new ArrayList<B>();",
818             "    list.add(b);",
819             "    return list;",
820             "  }",
821             "",
822             "  @Provides @IntoSet B provideBElement(B b) {",
823             "    return b;",
824             "  }",
825             "",
826             "  @Provides @IntoMap @StringKey(\"b\") B provideBEntry(B b) {",
827             "    return b;",
828             "  }",
829             "}");
830     Source numberChild =
831         CompilerTests.javaSource("test.ChildNumberModule",
832         "package test;",
833         "",
834         "import dagger.Module;",
835         "import dagger.Provides;",
836         "",
837         "@Module",
838         "class ChildNumberModule extends ParentModule<String, Number, Double> {",
839         "  @Provides Number provideNumber() { return 1; }",
840         "}");
841     Source integerChild =
842         CompilerTests.javaSource("test.ChildIntegerModule",
843         "package test;",
844         "",
845         "import dagger.Module;",
846         "import dagger.Provides;",
847         "",
848         "@Module",
849         "class ChildIntegerModule extends ParentModule<StringBuilder, Integer, Float> {",
850         "  @Provides Integer provideInteger() { return 2; }",
851         "}");
852     Source component =
853         CompilerTests.javaSource("test.C",
854         "package test;",
855         "",
856         "import dagger.Component;",
857         "import java.util.List;",
858         "",
859         "@Component(modules={ChildNumberModule.class, ChildIntegerModule.class})",
860         "interface C {",
861         "  List<Number> numberList();",
862         "  List<Integer> integerList();",
863         "}");
864     CompilerTests.daggerCompiler(parent, numberChild, integerChild, component)
865         .compile(
866             subject -> {
867               subject.hasErrorCount(0);
868               subject.generatedSource(
869                   goldenFileRule.goldenSource("test/ParentModule_ProvideListBFactory"));
870               subject.generatedSource(
871                   goldenFileRule.goldenSource("test/ParentModule_ProvideBElementFactory"));
872               subject.generatedSource(
873                   goldenFileRule.goldenSource("test/ParentModule_ProvideBEntryFactory"));
874               subject.generatedSource(
875                   goldenFileRule.goldenSource("test/ChildNumberModule_ProvideNumberFactory"));
876               subject.generatedSource(
877                   goldenFileRule.goldenSource("test/ChildIntegerModule_ProvideIntegerFactory"));
878             });
879   }
880 
parameterizedModuleWithStaticProvidesMethodOfGenericType()881   @Test public void parameterizedModuleWithStaticProvidesMethodOfGenericType() {
882     Source moduleFile =
883         CompilerTests.javaSource(
884             "test.ParameterizedModule",
885             "package test;",
886             "",
887             "import dagger.Module;",
888             "import dagger.Provides;",
889             "import java.util.List;",
890             "import java.util.ArrayList;",
891             "import java.util.Map;",
892             "import java.util.HashMap;",
893             "",
894             "@Module abstract class ParameterizedModule<T> {",
895             "  @Provides List<T> provideListT() {",
896             "    return new ArrayList<>();",
897             "  }",
898             "",
899             "  @Provides static Map<String, Number> provideMapStringNumber() {",
900             "    return new HashMap<>();",
901             "  }",
902             "",
903             "  @Provides static Object provideNonGenericType() {",
904             "    return new Object();",
905             "  }",
906             "",
907             "  @Provides static String provideNonGenericTypeWithDeps(Object o) {",
908             "    return o.toString();",
909             "  }",
910             "}");
911     CompilerTests.daggerCompiler(moduleFile)
912         .compile(
913             subject -> {
914               subject.hasErrorCount(0);
915               subject.generatedSource(
916                   goldenFileRule.goldenSource(
917                       "test/ParameterizedModule_ProvideMapStringNumberFactory"));
918               subject.generatedSource(
919                   goldenFileRule.goldenSource(
920                       "test/ParameterizedModule_ProvideNonGenericTypeFactory"));
921               subject.generatedSource(
922                   goldenFileRule.goldenSource(
923                       "test/ParameterizedModule_ProvideNonGenericTypeWithDepsFactory"));
924             });
925   }
926 
927   private static final Source QUALIFIER_A =
928         CompilerTests.javaSource(
929           "test.QualifierA",
930           "package test;",
931           "",
932           "import javax.inject.Qualifier;",
933           "",
934           "@Qualifier @interface QualifierA {}");
935 
936   private static final Source QUALIFIER_B =
937         CompilerTests.javaSource(
938           "test.QualifierB",
939           "package test;",
940           "",
941           "import javax.inject.Qualifier;",
942           "",
943           "@Qualifier @interface QualifierB {}");
944 
945   @Test
providesMethodMultipleQualifiersOnMethod()946   public void providesMethodMultipleQualifiersOnMethod() {
947     Source moduleFile =
948         CompilerTests.javaSource("test.TestModule",
949         "package test;",
950         "",
951         "import dagger.Module;",
952         "import dagger.Provides;",
953         "",
954         "@Module",
955         "final class TestModule {",
956         "  @Provides",
957         "  @QualifierA",
958         "  @QualifierB",
959         "  String provideString() {",
960         "    return \"foo\";",
961         "  }",
962         "}");
963     CompilerTests.daggerCompiler(moduleFile, QUALIFIER_A, QUALIFIER_B)
964         .compile(
965             subject -> {
966               // There are 2 errors -- 1 per qualifier.
967               subject.hasErrorCount(2);
968               subject.hasErrorContaining("may not use more than one @Qualifier")
969                   .onSource(moduleFile)
970                   .onLine(9);
971               subject.hasErrorContaining("may not use more than one @Qualifier")
972                   .onSource(moduleFile)
973                   .onLine(10);
974             });
975   }
976 
977   @Test
providesMethodMultipleQualifiersOnParameter()978   public void providesMethodMultipleQualifiersOnParameter() {
979     Source moduleFile =
980         CompilerTests.javaSource(
981             "test.TestModule",
982             "package test;",
983             "",
984             "import dagger.Module;",
985             "import dagger.Provides;",
986             "",
987             "@Module",
988             "final class TestModule {",
989             "  @Provides",
990             "  static String provideString(",
991             "      @QualifierA",
992             "      @QualifierB",
993             "      Object object) {",
994             "    return \"foo\";",
995             "  }",
996             "}");
997     CompilerTests.daggerCompiler(moduleFile, QUALIFIER_A, QUALIFIER_B)
998         .compile(
999             subject -> {
1000               // There are two errors -- 1 per qualifier.
1001               subject.hasErrorCount(2);
1002               subject.hasErrorContaining("may not use more than one @Qualifier")
1003                   .onSource(moduleFile)
1004                   .onLine(10);
1005               subject.hasErrorContaining("may not use more than one @Qualifier")
1006                   .onSource(moduleFile)
1007                   .onLine(11);
1008             });
1009   }
1010 
1011   @Test
providesMethodWildcardDependency()1012   public void providesMethodWildcardDependency() {
1013     Source moduleFile =
1014         CompilerTests.javaSource(
1015             "test.TestModule",
1016             "package test;",
1017             "",
1018             "import dagger.Module;",
1019             "import dagger.Provides;",
1020             "import javax.inject.Provider;",
1021             "",
1022             "@Module",
1023             "final class TestModule {",
1024             "  @Provides static String provideString(Provider<? extends Number> numberProvider) {",
1025             "    return \"foo\";",
1026             "  }",
1027             "}");
1028     CompilerTests.daggerCompiler(moduleFile, QUALIFIER_A, QUALIFIER_B)
1029         .compile(
1030             subject -> {
1031               subject.hasErrorCount(1);
1032               subject.hasErrorContaining(
1033                   "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, or "
1034                       + "Produced<T> when T is a wildcard type such as ? extends java.lang.Number");
1035             });
1036   }
1037 
1038   private static final Source SCOPE_A =
1039         CompilerTests.javaSource(
1040           "test.ScopeA",
1041           "package test;",
1042           "",
1043           "import javax.inject.Scope;",
1044           "",
1045           "@Scope @interface ScopeA {}");
1046 
1047   private static final Source SCOPE_B =
1048         CompilerTests.javaSource(
1049           "test.ScopeB",
1050           "package test;",
1051           "",
1052           "import javax.inject.Scope;",
1053           "",
1054           "@Scope @interface ScopeB {}");
1055 
1056   @Test
providesMethodMultipleScopes()1057   public void providesMethodMultipleScopes() {
1058     Source moduleFile =
1059         CompilerTests.javaSource(
1060             "test.TestModule",
1061             "package test;",
1062             "",
1063             "import dagger.Module;",
1064             "import dagger.Provides;",
1065             "",
1066             "@Module",
1067             "final class TestModule {",
1068             "  @Provides",
1069             "  @ScopeA",
1070             "  @ScopeB",
1071             "  String provideString() {",
1072             "    return \"foo\";",
1073             "  }",
1074             "}");
1075     CompilerTests.daggerCompiler(moduleFile, SCOPE_A, SCOPE_B)
1076         .compile(
1077             subject -> {
1078               subject.hasErrorCount(2);
1079               subject.hasErrorContaining("cannot use more than one @Scope")
1080                   .onSource(moduleFile)
1081                   .onLineContaining("@ScopeA");
1082               subject.hasErrorContaining("cannot use more than one @Scope")
1083                   .onSource(moduleFile)
1084                   .onLineContaining("@ScopeB");
1085             });
1086   }
1087 
providerDependsOnProduced()1088   @Test public void providerDependsOnProduced() {
1089     Source moduleFile =
1090         CompilerTests.javaSource(
1091             "test.TestModule",
1092             "package test;",
1093             "",
1094             "import dagger.Module;",
1095             "import dagger.Provides;",
1096             "import dagger.producers.Producer;",
1097             "",
1098             "@Module",
1099             "final class TestModule {",
1100             "  @Provides String provideString(Producer<Integer> producer) {",
1101             "    return \"foo\";",
1102             "  }",
1103             "}");
1104     CompilerTests.daggerCompiler(moduleFile)
1105         .compile(
1106             subject -> {
1107               subject.hasErrorCount(1);
1108               subject.hasErrorContaining("Producer may only be injected in @Produces methods");
1109             });
1110   }
1111 
providerDependsOnProducer()1112   @Test public void providerDependsOnProducer() {
1113     Source moduleFile =
1114         CompilerTests.javaSource(
1115             "test.TestModule",
1116             "package test;",
1117             "",
1118             "import dagger.Module;",
1119             "import dagger.Provides;",
1120             "import dagger.producers.Produced;",
1121             "",
1122             "@Module",
1123             "final class TestModule {",
1124             "  @Provides String provideString(Produced<Integer> produced) {",
1125             "    return \"foo\";",
1126             "  }",
1127             "}");
1128     CompilerTests.daggerCompiler(moduleFile)
1129         .compile(
1130             subject -> {
1131               subject.hasErrorCount(1);
1132               subject.hasErrorContaining("Produced may only be injected in @Produces methods");
1133             });
1134   }
1135 
1136   @Test
proxyMethodsConflictWithOtherFactoryMethods()1137   public void proxyMethodsConflictWithOtherFactoryMethods() throws Exception {
1138     Source module =
1139         CompilerTests.javaSource(
1140             "test.TestModule",
1141             "package test;",
1142             "",
1143             "import dagger.Module;",
1144             "import dagger.Provides;",
1145             "",
1146             "@Module",
1147             "interface TestModule {",
1148             "  @Provides",
1149             "  static int get() { return 1; }",
1150             "",
1151             "  @Provides",
1152             "  static boolean create() { return true; }",
1153             "}");
1154 
1155     CompilerTests.daggerCompiler(module)
1156         .compile(
1157             subject -> {
1158               subject.hasErrorCount(0);
1159               subject.generatedSource(goldenFileRule.goldenSource("test/TestModule_GetFactory"));
1160               subject.generatedSource(goldenFileRule.goldenSource("test/TestModule_CreateFactory"));
1161             });
1162   }
1163 
1164   @Test
testScopedMetadataOnStaticProvides()1165   public void testScopedMetadataOnStaticProvides() throws Exception {
1166     Source module =
1167         CompilerTests.javaSource(
1168             "test.ScopedBinding",
1169             "package test;",
1170             "",
1171             "import dagger.Module;",
1172             "import dagger.Provides;",
1173             "import javax.inject.Singleton;",
1174             "",
1175             "@Module",
1176             "interface MyModule {",
1177             "  @NonScope",
1178             "  @Singleton",
1179             "  @Provides",
1180             "  static String provideString() {",
1181             "    return \"\";",
1182             "  }",
1183             "}");
1184     Source nonScope =
1185         CompilerTests.javaSource(
1186             "test.NonScope",
1187             "package test;",
1188             "",
1189             "@interface NonScope {}");
1190 
1191     CompilerTests.daggerCompiler(module, nonScope)
1192         .compile(
1193             subject -> {
1194               subject.hasErrorCount(0);
1195               subject.generatedSource(
1196                   goldenFileRule.goldenSource("test/MyModule_ProvideStringFactory"));
1197             });
1198   }
1199 
1200   @Test
testScopedMetadataOnNonStaticProvides()1201   public void testScopedMetadataOnNonStaticProvides() throws Exception {
1202     Source module =
1203         CompilerTests.javaSource(
1204             "test.ScopedBinding",
1205             "package test;",
1206             "",
1207             "import dagger.Module;",
1208             "import dagger.Provides;",
1209             "import javax.inject.Singleton;",
1210             "",
1211             "@Module",
1212             "class MyModule {",
1213             "  @NonScope",
1214             "  @Singleton",
1215             "  @Provides",
1216             "  String provideString() {",
1217             "    return \"\";",
1218             "  }",
1219             "}");
1220     Source nonScope =
1221         CompilerTests.javaSource(
1222             "test.NonScope",
1223             "package test;",
1224             "",
1225             "@interface NonScope {}");
1226 
1227     CompilerTests.daggerCompiler(module, nonScope)
1228         .compile(
1229             subject -> {
1230               subject.hasErrorCount(0);
1231               subject.generatedSource(
1232                   goldenFileRule.goldenSource("test/MyModule_ProvideStringFactory"));
1233             });
1234   }
1235 
1236   @Test
testScopeMetadataWithCustomScope()1237   public void testScopeMetadataWithCustomScope() throws Exception {
1238     Source module =
1239         CompilerTests.javaSource(
1240             "test.ScopedBinding",
1241             "package test;",
1242             "",
1243             "import dagger.Module;",
1244             "import dagger.Provides;",
1245             "import javax.inject.Singleton;",
1246             "",
1247             "@Module",
1248             "interface MyModule {",
1249             "  @NonScope(\"someValue\")",
1250             "  @CustomScope(\"someOtherValue\")",
1251             "  @Provides",
1252             "  static String provideString() {",
1253             "    return \"\";",
1254             "  }",
1255             "}");
1256     Source customScope =
1257         CompilerTests.javaSource(
1258             "test.CustomScope",
1259             "package test;",
1260             "",
1261             "import javax.inject.Scope;",
1262             "",
1263             "@Scope",
1264             "@interface CustomScope {",
1265             "  String value();",
1266             "}");
1267     Source nonScope =
1268         CompilerTests.javaSource(
1269             "test.NonScope",
1270             "package test;",
1271             "",
1272             "@interface NonScope {",
1273             "  String value();",
1274             "}");
1275 
1276     CompilerTests.daggerCompiler(module, customScope, nonScope)
1277         .compile(
1278             subject -> {
1279               subject.hasErrorCount(0);
1280               subject.generatedSource(
1281                   goldenFileRule.goldenSource("test/MyModule_ProvideStringFactory"));
1282             });
1283   }
1284 
1285   @Test
testQualifierMetadataOnProvides()1286   public void testQualifierMetadataOnProvides() throws Exception {
1287     Source module =
1288         CompilerTests.javaSource(
1289             "test.ScopedBinding",
1290             "package test;",
1291             "",
1292             "import dagger.Module;",
1293             "import dagger.Provides;",
1294             "import javax.inject.Singleton;",
1295             "",
1296             "@Module",
1297             "interface MyModule {",
1298             "  @Provides",
1299             "  @NonQualifier",
1300             "  @MethodQualifier",
1301             "  static String provideString(@NonQualifier @ParamQualifier int i) {",
1302             "    return \"\";",
1303             "  }",
1304             "}");
1305     Source methodQualifier =
1306         CompilerTests.javaSource(
1307             "test.MethodQualifier",
1308             "package test;",
1309             "",
1310             "import javax.inject.Qualifier;",
1311             "",
1312             "@Qualifier",
1313             "@interface MethodQualifier {}");
1314     Source paramQualifier =
1315         CompilerTests.javaSource(
1316             "test.ParamQualifier",
1317             "package test;",
1318             "",
1319             "import javax.inject.Qualifier;",
1320             "",
1321             "@Qualifier",
1322             "@interface ParamQualifier {}");
1323     Source nonQualifier =
1324         CompilerTests.javaSource(
1325             "test.NonQualifier",
1326             "package test;",
1327             "",
1328             "@interface NonQualifier {}");
1329 
1330     CompilerTests.daggerCompiler(module, methodQualifier, paramQualifier, nonQualifier)
1331         .compile(
1332             subject -> {
1333               subject.hasErrorCount(0);
1334               subject.generatedSource(
1335                   goldenFileRule.goldenSource("test/MyModule_ProvideStringFactory"));
1336             });
1337   }
1338 
1339   private static final String BINDS_METHOD = "@Binds abstract Foo bindFoo(FooImpl impl);";
1340   private static final String MULTIBINDS_METHOD = "@Multibinds abstract Set<Foo> foos();";
1341   private static final String STATIC_PROVIDES_METHOD =
1342       "@Provides static Bar provideBar() { return new Bar(); }";
1343   private static final String INSTANCE_PROVIDES_METHOD =
1344       "@Provides Baz provideBaz() { return new Baz(); }";
1345   private static final String SOME_ABSTRACT_METHOD = "abstract void blah();";
1346 
1347   @Test
bindsWithInstanceProvides()1348   public void bindsWithInstanceProvides() {
1349     compileMethodCombination(BINDS_METHOD, INSTANCE_PROVIDES_METHOD)
1350         .compile(
1351             subject -> {
1352               subject.hasErrorCount(1);
1353               subject.hasErrorContaining(
1354                   "A @Module may not contain both non-static and abstract binding methods");
1355             });
1356   }
1357 
1358   @Test
multibindsWithInstanceProvides()1359   public void multibindsWithInstanceProvides() {
1360     compileMethodCombination(MULTIBINDS_METHOD, INSTANCE_PROVIDES_METHOD)
1361         .compile(
1362             subject -> {
1363               subject.hasErrorCount(1);
1364               subject.hasErrorContaining(
1365                   "A @Module may not contain both non-static and abstract binding methods");
1366             });
1367   }
1368 
1369   @Test
bindsWithStaticProvides()1370   public void bindsWithStaticProvides() {
1371     compileMethodCombination(BINDS_METHOD, STATIC_PROVIDES_METHOD)
1372         .compile(subject -> subject.hasErrorCount(0));
1373   }
1374 
1375   @Test
bindsWithMultibinds()1376   public void bindsWithMultibinds() {
1377     compileMethodCombination(BINDS_METHOD, MULTIBINDS_METHOD)
1378         .compile(subject -> subject.hasErrorCount(0));
1379   }
1380 
1381   @Test
multibindsWithStaticProvides()1382   public void multibindsWithStaticProvides() {
1383     compileMethodCombination(MULTIBINDS_METHOD, STATIC_PROVIDES_METHOD)
1384         .compile(subject -> subject.hasErrorCount(0));
1385   }
1386 
1387   @Test
instanceProvidesWithAbstractMethod()1388   public void instanceProvidesWithAbstractMethod() {
1389     compileMethodCombination(INSTANCE_PROVIDES_METHOD, SOME_ABSTRACT_METHOD)
1390         .compile(subject -> subject.hasErrorCount(0));
1391   }
1392 
compileMethodCombination(String... methodLines)1393   private CompilerTests.DaggerCompiler compileMethodCombination(String... methodLines) {
1394     Source fooFile =
1395         CompilerTests.javaSource(
1396             "test.Foo",
1397             "package test;",
1398             "",
1399             "interface Foo {}");
1400     Source fooImplFile =
1401         CompilerTests.javaSource(
1402             "test.FooImpl",
1403             "package test;",
1404             "",
1405             "import javax.inject.Inject;",
1406             "",
1407             "final class FooImpl implements Foo {",
1408             "  @Inject FooImpl() {}",
1409             "}");
1410     Source barFile =
1411         CompilerTests.javaSource(
1412             "test.Bar",
1413             "package test;",
1414             "",
1415             "final class Bar {}");
1416     Source bazFile =
1417         CompilerTests.javaSource(
1418             "test.Baz",
1419             "package test;",
1420             "",
1421             "final class Baz {}");
1422 
1423     ImmutableList<String> moduleLines =
1424         new ImmutableList.Builder<String>()
1425             .add(
1426                 "package test;",
1427                 "",
1428                 "import dagger.Binds;",
1429                 "import dagger.Module;",
1430                 "import dagger.Provides;",
1431                 "import dagger.multibindings.Multibinds;",
1432                 "import java.util.Set;",
1433                 "",
1434                 "@Module abstract class TestModule {")
1435             .add(methodLines)
1436             .add("}")
1437             .build();
1438 
1439     Source bindsMethodAndInstanceProvidesMethodModuleFile =
1440         CompilerTests.javaSource("test.TestModule", moduleLines);
1441     return CompilerTests.daggerCompiler(
1442         fooFile, fooImplFile, barFile, bazFile, bindsMethodAndInstanceProvidesMethodModuleFile);
1443   }
1444 }
1445