• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.Compilers.daggerCompiler;
21 import static dagger.internal.codegen.TestUtils.message;
22 
23 import com.google.testing.compile.Compilation;
24 import com.google.testing.compile.JavaFileObjects;
25 import javax.tools.JavaFileObject;
26 import org.junit.Test;
27 import org.junit.runner.RunWith;
28 import org.junit.runners.JUnit4;
29 
30 @RunWith(JUnit4.class)
31 public class MissingBindingValidationTest {
32   @Test
dependOnInterface()33   public void dependOnInterface() {
34     JavaFileObject component = JavaFileObjects.forSourceLines("test.MyComponent",
35         "package test;",
36         "",
37         "import dagger.Component;",
38         "",
39         "@Component",
40         "interface MyComponent {",
41         "  Foo getFoo();",
42         "}");
43     JavaFileObject injectable = JavaFileObjects.forSourceLines("test.Foo",
44         "package test;",
45         "",
46         "import javax.inject.Inject;",
47         "",
48         "class Foo {",
49         "  @Inject Foo(Bar bar) {}",
50         "}");
51     JavaFileObject nonInjectable = JavaFileObjects.forSourceLines("test.Bar",
52         "package test;",
53         "",
54         "import javax.inject.Inject;",
55         "",
56         "interface Bar {}");
57     Compilation compilation = daggerCompiler().compile(component, injectable, nonInjectable);
58     assertThat(compilation).failed();
59     assertThat(compilation).hadErrorCount(1);
60     assertThat(compilation)
61         .hadErrorContaining("Bar cannot be provided without an @Provides-annotated method.")
62         .inFile(component)
63         .onLineContaining("interface MyComponent");
64   }
65 
66   @Test
entryPointDependsOnInterface()67   public void entryPointDependsOnInterface() {
68     JavaFileObject component =
69         JavaFileObjects.forSourceLines(
70             "test.TestClass",
71             "package test;",
72             "",
73             "import dagger.Component;",
74             "",
75             "final class TestClass {",
76             "  interface A {}",
77             "",
78             "  @Component()",
79             "  interface AComponent {",
80             "    A getA();",
81             "  }",
82             "}");
83     Compilation compilation = daggerCompiler().compile(component);
84     assertThat(compilation).failed();
85     assertThat(compilation).hadErrorCount(1);
86     assertThat(compilation)
87         .hadErrorContaining(
88             "\033[1;31m[Dagger/MissingBinding]\033[0m TestClass.A cannot be provided "
89                 + "without an @Provides-annotated method.")
90         .inFile(component)
91         .onLineContaining("interface AComponent");
92   }
93 
94   @Test
entryPointDependsOnQualifiedInterface()95   public void entryPointDependsOnQualifiedInterface() {
96     JavaFileObject component =
97         JavaFileObjects.forSourceLines(
98             "test.TestClass",
99             "package test;",
100             "",
101             "import dagger.Component;",
102             "import javax.inject.Qualifier;",
103             "",
104             "final class TestClass {",
105             "  @Qualifier @interface Q {}",
106             "  interface A {}",
107             "",
108             "  @Component()",
109             "  interface AComponent {",
110             "    @Q A qualifiedA();",
111             "  }",
112             "}");
113     Compilation compilation = daggerCompiler().compile(component);
114     assertThat(compilation).failed();
115     assertThat(compilation).hadErrorCount(1);
116     assertThat(compilation)
117         .hadErrorContaining(
118             "\033[1;31m[Dagger/MissingBinding]\033[0m @TestClass.Q TestClass.A cannot be provided "
119                 + "without an @Provides-annotated method.")
120         .inFile(component)
121         .onLineContaining("interface AComponent");
122   }
123 
constructorInjectionWithoutAnnotation()124   @Test public void constructorInjectionWithoutAnnotation() {
125     JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
126         "package test;",
127         "",
128         "import dagger.Component;",
129         "import dagger.Module;",
130         "import dagger.Provides;",
131         "import javax.inject.Inject;",
132         "",
133         "final class TestClass {",
134         "  static class A {",
135         "    A() {}",
136         "  }",
137         "",
138         "  @Component()",
139         "  interface AComponent {",
140         "    A getA();",
141         "  }",
142         "}");
143 
144     Compilation compilation = daggerCompiler().compile(component);
145     assertThat(compilation).failed();
146     assertThat(compilation).hadErrorCount(1);
147     assertThat(compilation)
148         .hadErrorContaining(
149             "TestClass.A cannot be provided without an @Inject constructor or an "
150                 + "@Provides-annotated method.")
151         .inFile(component)
152         .onLineContaining("interface AComponent");
153   }
154 
membersInjectWithoutProvision()155   @Test public void membersInjectWithoutProvision() {
156     JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass",
157         "package test;",
158         "",
159         "import dagger.Component;",
160         "import dagger.Module;",
161         "import dagger.Provides;",
162         "import javax.inject.Inject;",
163         "",
164         "final class TestClass {",
165         "  static class A {",
166         "    @Inject A() {}",
167         "  }",
168         "",
169         "  static class B {",
170         "    @Inject A a;",
171         "  }",
172         "",
173         "  @Component()",
174         "  interface AComponent {",
175         "    B getB();",
176         "  }",
177         "}");
178 
179     Compilation compilation = daggerCompiler().compile(component);
180     assertThat(compilation).failed();
181     assertThat(compilation).hadErrorCount(1);
182     assertThat(compilation)
183         .hadErrorContaining(
184             "TestClass.B cannot be provided without an @Inject constructor or an "
185                 + "@Provides-annotated method. This type supports members injection but cannot be "
186                 + "implicitly provided.")
187         .inFile(component)
188         .onLineContaining("interface AComponent");
189   }
190 
191   @Test
missingBindingWithSameKeyAsMembersInjectionMethod()192   public void missingBindingWithSameKeyAsMembersInjectionMethod() {
193     JavaFileObject self =
194         JavaFileObjects.forSourceLines(
195             "test.Self",
196             "package test;",
197             "",
198             "import javax.inject.Inject;",
199             "import javax.inject.Provider;",
200             "",
201             "class Self {",
202             "  @Inject Provider<Self> selfProvider;",
203             "}");
204     JavaFileObject component =
205         JavaFileObjects.forSourceLines(
206             "test.SelfComponent",
207             "package test;",
208             "",
209             "import dagger.Component;",
210             "",
211             "@Component",
212             "interface SelfComponent {",
213             "  void inject(Self target);",
214             "}");
215 
216     Compilation compilation = daggerCompiler().compile(self, component);
217     assertThat(compilation).failed();
218     assertThat(compilation).hadErrorCount(1);
219     assertThat(compilation)
220         .hadErrorContaining("Self cannot be provided without an @Inject constructor")
221         .inFile(component)
222         .onLineContaining("interface SelfComponent");
223   }
224 
225   @Test
genericInjectClassWithWildcardDependencies()226   public void genericInjectClassWithWildcardDependencies() {
227     JavaFileObject component =
228         JavaFileObjects.forSourceLines(
229             "test.TestComponent",
230             "package test;",
231             "",
232             "import dagger.Component;",
233             "",
234             "@Component",
235             "interface TestComponent {",
236             "  Foo<? extends Number> foo();",
237             "}");
238     JavaFileObject foo =
239         JavaFileObjects.forSourceLines(
240             "test.Foo",
241             "package test;",
242             "",
243             "import javax.inject.Inject;",
244             "",
245             "final class Foo<T> {",
246             "  @Inject Foo(T t) {}",
247             "}");
248     Compilation compilation = daggerCompiler().compile(component, foo);
249     assertThat(compilation).failed();
250     assertThat(compilation).hadErrorCount(1);
251     assertThat(compilation)
252         .hadErrorContaining(
253             "Foo<? extends Number> cannot be provided "
254                 + "without an @Provides-annotated method");
255   }
256 
longChainOfDependencies()257   @Test public void longChainOfDependencies() {
258     JavaFileObject component =
259         JavaFileObjects.forSourceLines(
260             "test.TestClass",
261             "package test;",
262             "",
263             "import dagger.Component;",
264             "import dagger.Lazy;",
265             "import dagger.Module;",
266             "import dagger.Provides;",
267             "import javax.inject.Inject;",
268             "import javax.inject.Named;",
269             "import javax.inject.Provider;",
270             "",
271             "final class TestClass {",
272             "  interface A {}",
273             "",
274             "  static class B {",
275             "    @Inject B(A a) {}",
276             "  }",
277             "",
278             "  static class C {",
279             "    @Inject B b;",
280             "    @Inject C(X x) {}",
281             "  }",
282             "",
283             "  interface D { }",
284             "",
285             "  static class DImpl implements D {",
286             "    @Inject DImpl(C c, B b) {}",
287             "  }",
288             "",
289             "  static class X {",
290             "    @Inject X() {}",
291             "  }",
292             "",
293             "  @Module",
294             "  static class DModule {",
295             "    @Provides @Named(\"slim shady\") D d(X x1, DImpl impl, X x2) { return impl; }",
296             "  }",
297             "",
298             "  @Component(modules = { DModule.class })",
299             "  interface AComponent {",
300             "    @Named(\"slim shady\") D getFoo();",
301             "    C injectC(C c);",
302             "    Provider<C> cProvider();",
303             "    Lazy<C> lazyC();",
304             "    Provider<Lazy<C>> lazyCProvider();",
305             "  }",
306             "}");
307 
308     Compilation compilation = daggerCompiler().compile(component);
309     assertThat(compilation).failed();
310     assertThat(compilation).hadErrorCount(1);
311     assertThat(compilation)
312         .hadErrorContaining(
313             message(
314                 "TestClass.A cannot be provided without an @Provides-annotated method.",
315                 "    TestClass.A is injected at",
316                 "        TestClass.B(a)",
317                 "    TestClass.B is injected at",
318                 "        TestClass.C.b",
319                 "    TestClass.C is injected at",
320                 "        TestClass.AComponent.injectC(TestClass.C)",
321                 "The following other entry points also depend on it:",
322                 "    TestClass.AComponent.getFoo()",
323                 "    TestClass.AComponent.cProvider()",
324                 "    TestClass.AComponent.lazyC()",
325                 "    TestClass.AComponent.lazyCProvider()"))
326         .inFile(component)
327         .onLineContaining("interface AComponent");
328   }
329 
330   @Test
bindsMethodAppearsInTrace()331   public void bindsMethodAppearsInTrace() {
332     JavaFileObject component =
333         JavaFileObjects.forSourceLines(
334             "TestComponent",
335             "import dagger.Component;",
336             "",
337             "@Component(modules = TestModule.class)",
338             "interface TestComponent {",
339             "  TestInterface testInterface();",
340             "}");
341     JavaFileObject interfaceFile =
342         JavaFileObjects.forSourceLines("TestInterface", "interface TestInterface {}");
343     JavaFileObject implementationFile =
344         JavaFileObjects.forSourceLines(
345             "TestImplementation",
346             "import javax.inject.Inject;",
347             "",
348             "final class TestImplementation implements TestInterface {",
349             "  @Inject TestImplementation(String missingBinding) {}",
350             "}");
351     JavaFileObject module =
352         JavaFileObjects.forSourceLines(
353             "TestModule",
354             "import dagger.Binds;",
355             "import dagger.Module;",
356             "",
357             "@Module",
358             "interface TestModule {",
359             "  @Binds abstract TestInterface bindTestInterface(TestImplementation implementation);",
360             "}");
361 
362     Compilation compilation =
363         daggerCompiler().compile(component, module, interfaceFile, implementationFile);
364     assertThat(compilation).failed();
365     assertThat(compilation).hadErrorCount(1);
366     assertThat(compilation)
367         .hadErrorContaining(
368             message(
369                 "String cannot be provided without an @Inject constructor or an "
370                     + "@Provides-annotated method.",
371                 "    String is injected at",
372                 "        TestImplementation(missingBinding)",
373                 "    TestImplementation is injected at",
374                 "        TestModule.bindTestInterface(implementation)",
375                 "    TestInterface is requested at",
376                 "        TestComponent.testInterface()"))
377         .inFile(component)
378         .onLineContaining("interface TestComponent");
379   }
380 
resolvedParametersInDependencyTrace()381   @Test public void resolvedParametersInDependencyTrace() {
382     JavaFileObject generic = JavaFileObjects.forSourceLines("test.Generic",
383         "package test;",
384         "",
385         "import javax.inject.Inject;",
386         "import javax.inject.Provider;",
387         "",
388         "final class Generic<T> {",
389         "  @Inject Generic(T t) {}",
390         "}");
391     JavaFileObject testClass = JavaFileObjects.forSourceLines("test.TestClass",
392         "package test;",
393         "",
394         "import javax.inject.Inject;",
395         "import java.util.List;",
396         "",
397         "final class TestClass {",
398         "  @Inject TestClass(List list) {}",
399         "}");
400     JavaFileObject usesTest = JavaFileObjects.forSourceLines("test.UsesTest",
401         "package test;",
402         "",
403         "import javax.inject.Inject;",
404         "",
405         "final class UsesTest {",
406         "  @Inject UsesTest(Generic<TestClass> genericTestClass) {}",
407         "}");
408     JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
409         "package test;",
410         "",
411         "import dagger.Component;",
412         "",
413         "@Component",
414         "interface TestComponent {",
415         "  UsesTest usesTest();",
416         "}");
417 
418     Compilation compilation = daggerCompiler().compile(generic, testClass, usesTest, component);
419     assertThat(compilation).failed();
420     assertThat(compilation).hadErrorCount(1);
421     assertThat(compilation)
422         .hadErrorContaining(
423             message(
424                 "List cannot be provided without an @Provides-annotated method.",
425                 "    List is injected at",
426                 "        TestClass(list)",
427                 "    TestClass is injected at",
428                 "        Generic(t)",
429                 "    Generic<TestClass> is injected at",
430                 "        UsesTest(genericTestClass)",
431                 "    UsesTest is requested at",
432                 "        TestComponent.usesTest()"));
433   }
434 
resolvedVariablesInDependencyTrace()435   @Test public void resolvedVariablesInDependencyTrace() {
436     JavaFileObject generic = JavaFileObjects.forSourceLines("test.Generic",
437         "package test;",
438         "",
439         "import javax.inject.Inject;",
440         "import javax.inject.Provider;",
441         "",
442         "final class Generic<T> {",
443         "  @Inject T t;",
444         "  @Inject Generic() {}",
445         "}");
446     JavaFileObject testClass = JavaFileObjects.forSourceLines("test.TestClass",
447         "package test;",
448         "",
449         "import javax.inject.Inject;",
450         "import java.util.List;",
451         "",
452         "final class TestClass {",
453         "  @Inject TestClass(List list) {}",
454         "}");
455     JavaFileObject usesTest = JavaFileObjects.forSourceLines("test.UsesTest",
456         "package test;",
457         "",
458         "import javax.inject.Inject;",
459         "",
460         "final class UsesTest {",
461         "  @Inject UsesTest(Generic<TestClass> genericTestClass) {}",
462         "}");
463     JavaFileObject component = JavaFileObjects.forSourceLines("test.TestComponent",
464         "package test;",
465         "",
466         "import dagger.Component;",
467         "",
468         "@Component",
469         "interface TestComponent {",
470         "  UsesTest usesTest();",
471         "}");
472 
473     Compilation compilation = daggerCompiler().compile(generic, testClass, usesTest, component);
474     assertThat(compilation).failed();
475     assertThat(compilation).hadErrorCount(1);
476     assertThat(compilation)
477         .hadErrorContaining(
478             message(
479                 "List cannot be provided without an @Provides-annotated method.",
480                 "    List is injected at",
481                 "        TestClass(list)",
482                 "    TestClass is injected at",
483                 "        Generic.t",
484                 "    Generic<TestClass> is injected at",
485                 "        UsesTest(genericTestClass)",
486                 "    UsesTest is requested at",
487                 "        TestComponent.usesTest()"));
488   }
489 
490   @Test
bindingUsedOnlyInSubcomponentDependsOnBindingOnlyInSubcomponent()491   public void bindingUsedOnlyInSubcomponentDependsOnBindingOnlyInSubcomponent() {
492     JavaFileObject parent =
493         JavaFileObjects.forSourceLines(
494             "Parent",
495             "import dagger.Component;",
496             "",
497             "@Component(modules = ParentModule.class)",
498             "interface Parent {",
499             "  Child child();",
500             "}");
501     JavaFileObject parentModule =
502         JavaFileObjects.forSourceLines(
503             "ParentModule",
504             "import dagger.Module;",
505             "import dagger.Provides;",
506             "",
507             "@Module",
508             "class ParentModule {",
509             "  @Provides static Object needsString(String string) {",
510             "    return \"needs string: \" + string;",
511             "  }",
512             "}");
513     JavaFileObject child =
514         JavaFileObjects.forSourceLines(
515             "Child",
516             "import dagger.Subcomponent;",
517             "",
518             "@Subcomponent(modules = ChildModule.class)",
519             "interface Child {",
520             "  String string();",
521             "  Object needsString();",
522             "}");
523     JavaFileObject childModule =
524         JavaFileObjects.forSourceLines(
525             "ChildModule",
526             "import dagger.Module;",
527             "import dagger.Provides;",
528             "",
529             "@Module",
530             "class ChildModule {",
531             "  @Provides static String string() {",
532             "    return \"child string\";",
533             "  }",
534             "}");
535 
536     Compilation compilation = daggerCompiler().compile(parent, parentModule, child, childModule);
537     assertThat(compilation).failed();
538     assertThat(compilation).hadErrorCount(1);
539     assertThat(compilation)
540         .hadErrorContainingMatch(
541             "(?s)\\QString cannot be provided\\E.*\\Q[Child] Child.needsString()\\E")
542         .inFile(parent)
543         .onLineContaining("interface Parent");
544   }
545 
546   @Test
multibindingContributionBetweenAncestorComponentAndEntrypointComponent()547   public void multibindingContributionBetweenAncestorComponentAndEntrypointComponent() {
548     JavaFileObject parent =
549         JavaFileObjects.forSourceLines(
550             "Parent",
551             "import dagger.Component;",
552             "",
553             "@Component(modules = ParentModule.class)",
554             "interface Parent {",
555             "  Child child();",
556             "}");
557     JavaFileObject child =
558         JavaFileObjects.forSourceLines(
559             "Child",
560             "import dagger.Subcomponent;",
561             "",
562             "@Subcomponent(modules = ChildModule.class)",
563             "interface Child {",
564             "  Grandchild grandchild();",
565             "}");
566     JavaFileObject grandchild =
567         JavaFileObjects.forSourceLines(
568             "Grandchild",
569             "import dagger.Subcomponent;",
570             "",
571             "@Subcomponent",
572             "interface Grandchild {",
573             "  Object object();",
574             "}");
575 
576     JavaFileObject parentModule =
577         JavaFileObjects.forSourceLines(
578             "ParentModule",
579             "import dagger.Module;",
580             "import dagger.Provides;",
581             "import dagger.multibindings.IntoSet;",
582             "import java.util.Set;",
583             "",
584             "@Module",
585             "class ParentModule {",
586             "  @Provides static Object dependsOnSet(Set<String> strings) {",
587             "    return \"needs strings: \" + strings;",
588             "  }",
589             "",
590             "  @Provides @IntoSet static String contributesToSet() {",
591             "    return \"parent string\";",
592             "  }",
593             "",
594             "  @Provides int missingDependency(double dub) {",
595             "    return 4;",
596             "  }",
597             "}");
598     JavaFileObject childModule =
599         JavaFileObjects.forSourceLines(
600             "ChildModule",
601             "import dagger.Module;",
602             "import dagger.Provides;",
603             "import dagger.multibindings.IntoSet;",
604             "",
605             "@Module",
606             "class ChildModule {",
607             "  @Provides @IntoSet static String contributesToSet(int i) {",
608             "    return \"\" + i;",
609             "  }",
610             "}");
611     Compilation compilation =
612         daggerCompiler().compile(parent, parentModule, child, childModule, grandchild);
613     assertThat(compilation).failed();
614     assertThat(compilation).hadErrorCount(1);
615     assertThat(compilation)
616         .hadErrorContainingMatch(
617             "(?s)\\QDouble cannot be provided\\E.*"
618                 + "\\QGrandchild.object() [Parent → Child → Grandchild]\\E$")
619         .inFile(parent)
620         .onLineContaining("interface Parent");
621   }
622 
623   @Test
manyDependencies()624   public void manyDependencies() {
625     JavaFileObject component =
626         JavaFileObjects.forSourceLines(
627             "test.TestComponent",
628             "package test;",
629             "",
630             "import dagger.Component;",
631             "",
632             "@Component(modules = TestModule.class)",
633             "interface TestComponent {",
634             "  Object object();",
635             "  String string();",
636             "}");
637     JavaFileObject module =
638         JavaFileObjects.forSourceLines(
639             "test.TestModule",
640             "package test;",
641             "",
642             "import dagger.Binds;",
643             "import dagger.Module;",
644             "import dagger.Provides;",
645             "",
646             "@Module",
647             "abstract class TestModule {",
648             "  @Binds abstract Object object(NotBound notBound);",
649             "",
650             "  @Provides static String string(NotBound notBound, Object object) {",
651             "    return notBound.toString();",
652             "  }",
653             "}");
654     JavaFileObject notBound =
655         JavaFileObjects.forSourceLines(
656             "test.NotBound", //
657             "package test;",
658             "",
659             "interface NotBound {}");
660     Compilation compilation = daggerCompiler().compile(component, module, notBound);
661     assertThat(compilation).failed();
662     assertThat(compilation).hadErrorCount(1);
663     assertThat(compilation)
664         .hadErrorContaining(
665             message(
666                 "\033[1;31m[Dagger/MissingBinding]\033[0m "
667                     + "NotBound cannot be provided without an @Provides-annotated method.",
668                 "    NotBound is injected at",
669                 "        TestModule.object(notBound)",
670                 "    Object is requested at",
671                 "        TestComponent.object()",
672                 "It is also requested at:",
673                 "    TestModule.string(notBound, …)",
674                 "The following other entry points also depend on it:",
675                 "    TestComponent.string()"))
676         .inFile(component)
677         .onLineContaining("interface TestComponent");
678     assertThat(compilation).hadErrorCount(1);
679   }
680 
681   @Test
tooManyRequests()682   public void tooManyRequests() {
683     JavaFileObject foo =
684         JavaFileObjects.forSourceLines(
685             "test.Foo",
686             "package test;",
687             "",
688             "import javax.inject.Inject;",
689             "",
690             "final class Foo {",
691             "  @Inject Foo(",
692             "      String one,",
693             "      String two,",
694             "      String three,",
695             "      String four,",
696             "      String five,",
697             "      String six,",
698             "      String seven,",
699             "      String eight,",
700             "      String nine,",
701             "      String ten,",
702             "      String eleven,",
703             "      String twelve,",
704             "      String thirteen) {",
705             "  }",
706             "}");
707     JavaFileObject component =
708         JavaFileObjects.forSourceLines(
709             "test.TestComponent",
710             "package test;",
711             "",
712             "import dagger.Component;",
713             "",
714             "@Component",
715             "interface TestComponent {",
716             "  String string();",
717             "  Foo foo();",
718             "}");
719 
720     Compilation compilation = daggerCompiler().compile(foo, component);
721     assertThat(compilation).failed();
722     assertThat(compilation).hadErrorCount(1);
723     assertThat(compilation)
724         .hadErrorContaining(
725             message(
726                 "\033[1;31m[Dagger/MissingBinding]\033[0m String cannot be provided without an "
727                     + "@Inject constructor or an @Provides-annotated method.",
728                 "    String is requested at",
729                 "        TestComponent.string()",
730                 "It is also requested at:",
731                 "    Foo(one, …)",
732                 "    Foo(…, two, …)",
733                 "    Foo(…, three, …)",
734                 "    Foo(…, four, …)",
735                 "    Foo(…, five, …)",
736                 "    Foo(…, six, …)",
737                 "    Foo(…, seven, …)",
738                 "    Foo(…, eight, …)",
739                 "    Foo(…, nine, …)",
740                 "    Foo(…, ten, …)",
741                 "    and 3 others"))
742         .inFile(component)
743         .onLineContaining("interface TestComponent");
744   }
745 
746   @Test
tooManyEntryPoints()747   public void tooManyEntryPoints() {
748     JavaFileObject component =
749         JavaFileObjects.forSourceLines(
750             "test.TestComponent",
751             "package test;",
752             "",
753             "import dagger.Component;",
754             "",
755             "@Component",
756             "interface TestComponent {",
757             "  String string1();",
758             "  String string2();",
759             "  String string3();",
760             "  String string4();",
761             "  String string5();",
762             "  String string6();",
763             "  String string7();",
764             "  String string8();",
765             "  String string9();",
766             "  String string10();",
767             "  String string11();",
768             "  String string12();",
769             "}");
770 
771     Compilation compilation = daggerCompiler().compile(component);
772     assertThat(compilation).failed();
773     assertThat(compilation).hadErrorCount(1);
774     assertThat(compilation)
775         .hadErrorContaining(
776             message(
777                 "\033[1;31m[Dagger/MissingBinding]\033[0m String cannot be provided without an "
778                     + "@Inject constructor or an @Provides-annotated method.",
779                 "    String is requested at",
780                 "        TestComponent.string1()",
781                 "The following other entry points also depend on it:",
782                 "    TestComponent.string2()",
783                 "    TestComponent.string3()",
784                 "    TestComponent.string4()",
785                 "    TestComponent.string5()",
786                 "    TestComponent.string6()",
787                 "    TestComponent.string7()",
788                 "    TestComponent.string8()",
789                 "    TestComponent.string9()",
790                 "    TestComponent.string10()",
791                 "    TestComponent.string11()",
792                 "    and 1 other"))
793         .inFile(component)
794         .onLineContaining("interface TestComponent");
795   }
796 
797   @Test
missingBindingInAllComponentsAndEntryPoints()798   public void missingBindingInAllComponentsAndEntryPoints() {
799     JavaFileObject parent =
800         JavaFileObjects.forSourceLines(
801             "Parent",
802             "import dagger.Component;",
803             "",
804             "@Component",
805             "interface Parent {",
806             "  Foo foo();",
807             "  Bar bar();",
808             "  Child child();",
809             "}");
810     JavaFileObject child =
811         JavaFileObjects.forSourceLines(
812             "Child",
813             "import dagger.Subcomponent;",
814             "",
815             "@Subcomponent",
816             "interface Child {",
817             "  Foo foo();",
818             "  Baz baz();",
819             "}");
820     JavaFileObject foo =
821         JavaFileObjects.forSourceLines(
822             "Foo",
823             "import javax.inject.Inject;",
824             "",
825             "class Foo {",
826             "  @Inject Foo(Bar bar) {}",
827             "}");
828     JavaFileObject bar =
829         JavaFileObjects.forSourceLines(
830             "Bar",
831             "import javax.inject.Inject;",
832             "",
833             "class Bar {",
834             "  @Inject Bar(Baz baz) {}",
835             "}");
836     JavaFileObject baz = JavaFileObjects.forSourceLines("Baz", "class Baz {}");
837 
838     Compilation compilation = daggerCompiler().compile(parent, child, foo, bar, baz);
839     assertThat(compilation).failed();
840     assertThat(compilation).hadErrorCount(1);
841     assertThat(compilation)
842         .hadErrorContaining(
843             message(
844                 "\033[1;31m[Dagger/MissingBinding]\033[0m Baz cannot be provided without an "
845                     + "@Inject constructor or an @Provides-annotated method.",
846                 "    Baz is injected at",
847                 "        Bar(baz)",
848                 "    Bar is requested at",
849                 "        Parent.bar()",
850                 "The following other entry points also depend on it:",
851                 "    Parent.foo()",
852                 "    Child.foo() [Parent → Child]",
853                 "    Child.baz() [Parent → Child]"))
854         .inFile(parent)
855         .onLineContaining("interface Parent");
856   }
857 
858   // Regression test for b/147423208 where if the same subcomponent was used
859   // in two different parts of the hierarchy and only one side had a missing binding
860   // incorrect caching during binding graph conversion might cause validation to pass
861   // incorrectly.
862   @Test
sameSubcomponentUsedInDifferentHierarchies()863   public void sameSubcomponentUsedInDifferentHierarchies() {
864     JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent",
865         "package test;",
866         "",
867         "import dagger.Component;",
868         "",
869         "@Component",
870         "interface Parent {",
871         "  Child1 getChild1();",
872         "  Child2 getChild2();",
873         "}");
874     JavaFileObject child1 = JavaFileObjects.forSourceLines("test.Child1",
875         "package test;",
876         "",
877         "import dagger.Subcomponent;",
878         "",
879         "@Subcomponent(modules = LongModule.class)",
880         "interface Child1 {",
881         "  RepeatedSub getSub();",
882         "}");
883     JavaFileObject child2 = JavaFileObjects.forSourceLines("test.Child2",
884         "package test;",
885         "",
886         "import dagger.Subcomponent;",
887         "",
888         "@Subcomponent",
889         "interface Child2 {",
890         "  RepeatedSub getSub();",
891         "}");
892     JavaFileObject repeatedSub = JavaFileObjects.forSourceLines("test.RepeatedSub",
893         "package test;",
894         "",
895         "import dagger.Subcomponent;",
896         "",
897         "@Subcomponent",
898         "interface RepeatedSub {",
899         "  Foo getFoo();",
900         "}");
901     JavaFileObject injectable = JavaFileObjects.forSourceLines("test.Foo",
902         "package test;",
903         "",
904         "import javax.inject.Inject;",
905         "",
906         "class Foo {",
907         "  @Inject Foo(Long value) {}",
908         "}");
909     JavaFileObject module = JavaFileObjects.forSourceLines("test.LongModule",
910         "package test;",
911         "",
912         "import dagger.Module;",
913         "import dagger.Provides;",
914         "",
915         "@Module",
916         "interface LongModule {",
917         "  @Provides static Long provideLong() {",
918         "    return 0L;",
919         "  }",
920         "}");
921     Compilation compilation = daggerCompiler().compile(
922         parent, child1, child2, repeatedSub, injectable, module);
923     assertThat(compilation).failed();
924     assertThat(compilation).hadErrorCount(1);
925     assertThat(compilation)
926         .hadErrorContaining("Long cannot be provided without an @Inject constructor")
927         .inFile(parent)
928         .onLineContaining("interface Parent");
929   }
930 
931   @Test
sameSubcomponentUsedInDifferentHierarchiesMissingBindingFromOneSide()932   public void sameSubcomponentUsedInDifferentHierarchiesMissingBindingFromOneSide() {
933     JavaFileObject parent =
934         JavaFileObjects.forSourceLines(
935             "test.Parent",
936             "package test;",
937             "",
938             "import dagger.Component;",
939             "",
940             "@Component",
941             "interface Parent {",
942             "  Child1 getChild1();",
943             "  Child2 getChild2();",
944             "}");
945     JavaFileObject child1 =
946         JavaFileObjects.forSourceLines(
947             "test.Child1",
948             "package test;",
949             "",
950             "import dagger.Subcomponent;",
951             "",
952             "@Subcomponent(modules = Child1Module.class)",
953             "interface Child1 {",
954             "  RepeatedSub getSub();",
955             "}");
956     JavaFileObject child2 =
957         JavaFileObjects.forSourceLines(
958             "test.Child2",
959             "package test;",
960             "",
961             "import dagger.Subcomponent;",
962             "",
963             "@Subcomponent(modules = Child2Module.class)",
964             "interface Child2 {",
965             "  RepeatedSub getSub();",
966             "}");
967     JavaFileObject repeatedSub =
968         JavaFileObjects.forSourceLines(
969             "test.RepeatedSub",
970             "package test;",
971             "",
972             "import dagger.Subcomponent;",
973             "",
974             "@Subcomponent(modules = RepeatedSubModule.class)",
975             "interface RepeatedSub {",
976             "  Object getObject();",
977             "}");
978     JavaFileObject child1Module =
979         JavaFileObjects.forSourceLines(
980             "test.Child1Module",
981             "package test;",
982             "",
983             "import dagger.Module;",
984             "import dagger.Provides;",
985             "import java.util.Set;",
986             "import dagger.multibindings.Multibinds;",
987             "",
988             "@Module",
989             "interface Child1Module {",
990             "  @Multibinds Set<Integer> multibindIntegerSet();",
991             "}");
992     JavaFileObject child2Module =
993         JavaFileObjects.forSourceLines(
994             "test.Child2Module",
995             "package test;",
996             "",
997             "import dagger.Module;",
998             "import dagger.Provides;",
999             "import java.util.Set;",
1000             "import dagger.multibindings.Multibinds;",
1001             "",
1002             "@Module",
1003             "interface Child2Module {",
1004             "  @Multibinds Set<Integer> multibindIntegerSet();",
1005             "",
1006             "  @Provides",
1007             "  static Object provideObject(Set<Integer> intSet) {",
1008             "    return new Object();",
1009             "  }",
1010             "}");
1011     JavaFileObject repeatedSubModule =
1012         JavaFileObjects.forSourceLines(
1013             "test.RepeatedSubModule",
1014             "package test;",
1015             "",
1016             "import dagger.Module;",
1017             "import dagger.Provides;",
1018             "import dagger.multibindings.IntoSet;",
1019             "import java.util.Set;",
1020             "import dagger.multibindings.Multibinds;",
1021             "",
1022             "@Module",
1023             "interface RepeatedSubModule {",
1024             "  @Provides",
1025             "  @IntoSet",
1026             "  static Integer provideInt() {",
1027             "    return 9;",
1028             "  }",
1029             "}");
1030 
1031     Compilation compilation =
1032         daggerCompiler()
1033             .compile(
1034                 parent, child1, child2, repeatedSub, child1Module, child2Module, repeatedSubModule);
1035     assertThat(compilation).failed();
1036     assertThat(compilation).hadErrorCount(1);
1037     assertThat(compilation)
1038         .hadErrorContaining("A binding for Object exists in [Parent → Child2 → RepeatedSub]:");
1039     assertThat(compilation)
1040         .hadErrorContaining(
1041             "[Parent → Child1 → RepeatedSub] RepeatedSub.getObject() [Parent → Child1 →"
1042                 + " RepeatedSub]");
1043   }
1044 
1045   @Test
differentComponentPkgSameSimpleNameMissingBinding()1046   public void differentComponentPkgSameSimpleNameMissingBinding() {
1047     JavaFileObject parent =
1048         JavaFileObjects.forSourceLines(
1049             "test.Parent",
1050             "package test;",
1051             "",
1052             "import dagger.Component;",
1053             "",
1054             "@Component",
1055             "interface Parent {",
1056             "  Child1 getChild1();",
1057             "  Child2 getChild2();",
1058             "}");
1059     JavaFileObject child1 =
1060         JavaFileObjects.forSourceLines(
1061             "test.Child1",
1062             "package test;",
1063             "",
1064             "import dagger.Subcomponent;",
1065             "",
1066             "@Subcomponent(modules = Child1Module.class)",
1067             "interface Child1 {",
1068             "  foo.Sub getSub();",
1069             "}");
1070     JavaFileObject child2 =
1071         JavaFileObjects.forSourceLines(
1072             "test.Child2",
1073             "package test;",
1074             "",
1075             "import dagger.Subcomponent;",
1076             "",
1077             "@Subcomponent(modules = Child2Module.class)",
1078             "interface Child2 {",
1079             "  bar.Sub getSub();",
1080             "}");
1081     JavaFileObject sub1 =
1082         JavaFileObjects.forSourceLines(
1083             "foo.Sub",
1084             "package foo;",
1085             "",
1086             "import dagger.Subcomponent;",
1087             "",
1088             "@Subcomponent(modules = test.RepeatedSubModule.class)",
1089             "public interface Sub {",
1090             "  Object getObject();",
1091             "}");
1092     JavaFileObject sub2 =
1093         JavaFileObjects.forSourceLines(
1094             "bar.Sub",
1095             "package bar;",
1096             "",
1097             "import dagger.Subcomponent;",
1098             "",
1099             "@Subcomponent(modules = test.RepeatedSubModule.class)",
1100             "public interface Sub {",
1101             "  Object getObject();",
1102             "}");
1103     JavaFileObject child1Module =
1104         JavaFileObjects.forSourceLines(
1105             "test.Child1Module",
1106             "package test;",
1107             "",
1108             "import dagger.Module;",
1109             "import dagger.Provides;",
1110             "import java.util.Set;",
1111             "import dagger.multibindings.Multibinds;",
1112             "",
1113             "@Module",
1114             "interface Child1Module {",
1115             "  @Multibinds Set<Integer> multibindIntegerSet();",
1116             "}");
1117     JavaFileObject child2Module =
1118         JavaFileObjects.forSourceLines(
1119             "test.Child2Module",
1120             "package test;",
1121             "",
1122             "import dagger.Module;",
1123             "import dagger.Provides;",
1124             "import java.util.Set;",
1125             "import dagger.multibindings.Multibinds;",
1126             "",
1127             "@Module",
1128             "interface Child2Module {",
1129             "  @Multibinds Set<Integer> multibindIntegerSet();",
1130             "",
1131             "  @Provides",
1132             "  static Object provideObject(Set<Integer> intSet) {",
1133             "    return new Object();",
1134             "  }",
1135             "}");
1136     JavaFileObject repeatedSubModule =
1137         JavaFileObjects.forSourceLines(
1138             "test.RepeatedSubModule",
1139             "package test;",
1140             "",
1141             "import dagger.Module;",
1142             "import dagger.Provides;",
1143             "import dagger.multibindings.IntoSet;",
1144             "import java.util.Set;",
1145             "import dagger.multibindings.Multibinds;",
1146             "",
1147             "@Module",
1148             "public interface RepeatedSubModule {",
1149             "  @Provides",
1150             "  @IntoSet",
1151             "  static Integer provideInt() {",
1152             "    return 9;",
1153             "  }",
1154             "}");
1155 
1156     Compilation compilation =
1157         daggerCompiler()
1158             .compile(
1159                 parent, child1, child2, sub1, sub2, child1Module, child2Module, repeatedSubModule);
1160     assertThat(compilation).failed();
1161     assertThat(compilation).hadErrorCount(1);
1162     assertThat(compilation).hadErrorContaining("A binding for Object exists in bar.Sub:");
1163     assertThat(compilation)
1164         .hadErrorContaining("[foo.Sub] foo.Sub.getObject() [Parent → Child1 → foo.Sub]");
1165   }
1166 }
1167