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.compilerWithOptions;
21 import static dagger.internal.codegen.Compilers.daggerCompiler;
22 import static dagger.internal.codegen.TestUtils.message;
23 import static org.junit.Assume.assumeFalse;
24 
25 import com.google.common.collect.ImmutableList;
26 import com.google.testing.compile.Compilation;
27 import com.google.testing.compile.JavaFileObjects;
28 import javax.tools.JavaFileObject;
29 import org.junit.Test;
30 import org.junit.runner.RunWith;
31 import org.junit.runners.Parameterized;
32 import org.junit.runners.Parameterized.Parameters;
33 
34 @RunWith(Parameterized.class)
35 public class DuplicateBindingsValidationTest {
36 
37   @Parameters(name = "fullBindingGraphValidation={0}")
parameters()38   public static ImmutableList<Object[]> parameters() {
39     return ImmutableList.copyOf(new Object[][] {{false}, {true}});
40   }
41 
42   private final boolean fullBindingGraphValidation;
43 
DuplicateBindingsValidationTest(boolean fullBindingGraphValidation)44   public DuplicateBindingsValidationTest(boolean fullBindingGraphValidation) {
45     this.fullBindingGraphValidation = fullBindingGraphValidation;
46   }
47 
duplicateExplicitBindings_ProvidesAndComponentProvision()48   @Test public void duplicateExplicitBindings_ProvidesAndComponentProvision() {
49     assumeFalse(fullBindingGraphValidation);
50 
51     JavaFileObject component = JavaFileObjects.forSourceLines("test.Outer",
52         "package test;",
53         "",
54         "import dagger.Component;",
55         "import dagger.Module;",
56         "import dagger.Provides;",
57         "",
58         "final class Outer {",
59         "  interface A {}",
60         "",
61         "  interface B {}",
62         "",
63         "  @Module",
64         "  static class AModule {",
65         "    @Provides String provideString() { return \"\"; }",
66         "    @Provides A provideA(String s) { return new A() {}; }",
67         "  }",
68         "",
69         "  @Component(modules = AModule.class)",
70         "  interface Parent {",
71         "    A getA();",
72         "  }",
73         "",
74         "  @Module",
75         "  static class BModule {",
76         "    @Provides B provideB(A a) { return new B() {}; }",
77         "  }",
78         "",
79         "  @Component(dependencies = Parent.class, modules = { BModule.class, AModule.class})",
80         "  interface Child {",
81         "    B getB();",
82         "  }",
83         "}");
84 
85     Compilation compilation =
86         compilerWithOptions(
87                 fullBindingGraphValidationOption())
88             .compile(component);
89     assertThat(compilation).failed();
90     assertThat(compilation)
91         .hadErrorContaining(
92             message(
93                 "Outer.A is bound multiple times:",
94                 "    @Provides Outer.A Outer.AModule.provideA(String)",
95                 "    Outer.A Outer.Parent.getA()"))
96         .inFile(component)
97         .onLineContaining("interface Child");
98   }
99 
duplicateExplicitBindings_TwoProvidesMethods()100   @Test public void duplicateExplicitBindings_TwoProvidesMethods() {
101     JavaFileObject component =
102         JavaFileObjects.forSourceLines(
103             "test.Outer",
104             "package test;",
105             "",
106             "import dagger.Component;",
107             "import dagger.Module;",
108             "import dagger.Provides;",
109             "import javax.inject.Inject;",
110             "",
111             "final class Outer {",
112             "  interface A {}",
113             "",
114             "  static class B {",
115             "    @Inject B(A a) {}",
116             "  }",
117             "",
118             "  @Module",
119             "  static class Module1 {",
120             "    @Provides A provideA1() { return new A() {}; }",
121             "  }",
122             "",
123             "  @Module",
124             "  static class Module2 {",
125             "    @Provides String provideString() { return \"\"; }",
126             "    @Provides A provideA2(String s) { return new A() {}; }",
127             "  }",
128             "",
129             "  @Module(includes = { Module1.class, Module2.class})",
130             "  abstract static class Module3 {}",
131             "",
132             "  @Component(modules = { Module1.class, Module2.class})",
133             "  interface TestComponent {",
134             "    A getA();",
135             "    B getB();",
136             "  }",
137             "}");
138 
139     Compilation compilation =
140         compilerWithOptions(
141                 fullBindingGraphValidationOption())
142             .compile(component);
143     assertThat(compilation).failed();
144     assertThat(compilation)
145         .hadErrorContaining(
146             message(
147                 "Outer.A is bound multiple times:",
148                 "    @Provides Outer.A Outer.Module1.provideA1()",
149                 "    @Provides Outer.A Outer.Module2.provideA2(String)"))
150         .inFile(component)
151         .onLineContaining("interface TestComponent");
152 
153     if (fullBindingGraphValidation) {
154       assertThat(compilation)
155           .hadErrorContaining(
156               message(
157                   "Outer.A is bound multiple times:",
158                   "    @Provides Outer.A Outer.Module1.provideA1()",
159                   "    @Provides Outer.A Outer.Module2.provideA2(String)"))
160           .inFile(component)
161           .onLineContaining("class Module3");
162     }
163 
164     // The duplicate bindngs are also requested from B, but we don't want to report them again.
165     assertThat(compilation).hadErrorCount(fullBindingGraphValidation ? 2 : 1);
166   }
167 
168   @Test
duplicateExplicitBindings_ProvidesVsBinds()169   public void duplicateExplicitBindings_ProvidesVsBinds() {
170     JavaFileObject component =
171         JavaFileObjects.forSourceLines(
172             "test.Outer",
173             "package test;",
174             "",
175             "import dagger.Binds;",
176             "import dagger.Component;",
177             "import dagger.Module;",
178             "import dagger.Provides;",
179             "import javax.inject.Inject;",
180             "",
181             "final class Outer {",
182             "  interface A {}",
183             "",
184             "  static final class B implements A {",
185             "    @Inject B() {}",
186             "  }",
187             "",
188             "  @Module",
189             "  static class Module1 {",
190             "    @Provides A provideA1() { return new A() {}; }",
191             "  }",
192             "",
193             "  @Module",
194             "  static abstract class Module2 {",
195             "    @Binds abstract A bindA2(B b);",
196             "  }",
197             "",
198             "  @Module(includes = { Module1.class, Module2.class})",
199             "  abstract static class Module3 {}",
200             "",
201             "  @Component(modules = { Module1.class, Module2.class})",
202             "  interface TestComponent {",
203             "    A getA();",
204             "  }",
205             "}");
206 
207     Compilation compilation =
208         compilerWithOptions(
209                 fullBindingGraphValidationOption())
210             .compile(component);
211     assertThat(compilation).failed();
212     assertThat(compilation)
213         .hadErrorContaining(
214             message(
215                 "Outer.A is bound multiple times:",
216                 "    @Provides Outer.A Outer.Module1.provideA1()",
217                 "    @Binds Outer.A Outer.Module2.bindA2(Outer.B)"))
218         .inFile(component)
219         .onLineContaining("interface TestComponent");
220 
221     if (fullBindingGraphValidation) {
222       assertThat(compilation)
223           .hadErrorContaining(
224               message(
225                   "Outer.A is bound multiple times:",
226                   "    @Provides Outer.A Outer.Module1.provideA1()",
227                   "    @Binds Outer.A Outer.Module2.bindA2(Outer.B)"))
228           .inFile(component)
229           .onLineContaining("class Module3");
230     }
231   }
232 
233   @Test
duplicateExplicitBindings_multibindingsAndExplicitSets()234   public void duplicateExplicitBindings_multibindingsAndExplicitSets() {
235     JavaFileObject component =
236         JavaFileObjects.forSourceLines(
237             "test.Outer",
238             "package test;",
239             "",
240             "import dagger.Binds;",
241             "import dagger.Component;",
242             "import dagger.Module;",
243             "import dagger.Provides;",
244             "import dagger.multibindings.IntoSet;",
245             "import java.util.HashSet;",
246             "import java.util.Set;",
247             "import javax.inject.Qualifier;",
248             "",
249             "final class Outer {",
250             "  @Qualifier @interface SomeQualifier {}",
251             "",
252             "  @Module",
253             "  abstract static class TestModule1 {",
254             "    @Provides @IntoSet static String stringSetElement() { return \"\"; }",
255             "",
256             "    @Binds",
257             "    @IntoSet abstract String bindStringSetElement(@SomeQualifier String value);",
258             "",
259             "    @Provides @SomeQualifier",
260             "    static String provideSomeQualifiedString() { return \"\"; }",
261             "  }",
262             "",
263             "  @Module",
264             "  static class TestModule2 {",
265             "    @Provides Set<String> stringSet() { return new HashSet<String>(); }",
266             "  }",
267             "",
268             "  @Module(includes = { TestModule1.class, TestModule2.class})",
269             "  abstract static class TestModule3 {}",
270             "",
271             "  @Component(modules = { TestModule1.class, TestModule2.class })",
272             "  interface TestComponent {",
273             "    Set<String> getStringSet();",
274             "  }",
275             "}");
276 
277     Compilation compilation =
278         compilerWithOptions(
279                 fullBindingGraphValidationOption())
280             .compile(component);
281     assertThat(compilation).failed();
282     assertThat(compilation)
283         .hadErrorContaining(
284             message(
285                 "Set<String> has incompatible bindings or declarations:",
286                 "    Set bindings and declarations:",
287                 "        @Binds @IntoSet String "
288                     + "Outer.TestModule1.bindStringSetElement(@Outer.SomeQualifier "
289                     + "String)",
290                 "        @Provides @IntoSet String "
291                     + "Outer.TestModule1.stringSetElement()",
292                 "    Unique bindings and declarations:",
293                 "        @Provides Set<String> Outer.TestModule2.stringSet()"))
294         .inFile(component)
295         .onLineContaining(
296             fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
297   }
298 
299   @Test
duplicateExplicitBindings_multibindingsAndExplicitMaps()300   public void duplicateExplicitBindings_multibindingsAndExplicitMaps() {
301     JavaFileObject component =
302         JavaFileObjects.forSourceLines(
303             "test.Outer",
304             "package test;",
305             "",
306             "import dagger.Binds;",
307             "import dagger.Component;",
308             "import dagger.Module;",
309             "import dagger.Provides;",
310             "import dagger.multibindings.IntoMap;",
311             "import dagger.multibindings.StringKey;",
312             "import java.util.HashMap;",
313             "import java.util.Map;",
314             "import javax.inject.Qualifier;",
315             "",
316             "final class Outer {",
317             "  @Qualifier @interface SomeQualifier {}",
318             "",
319             "  @Module",
320             "  abstract static class TestModule1 {",
321             "    @Provides @IntoMap",
322             "    @StringKey(\"foo\")",
323             "    static String stringMapEntry() { return \"\"; }",
324             "",
325             "    @Binds @IntoMap @StringKey(\"bar\")",
326             "    abstract String bindStringMapEntry(@SomeQualifier String value);",
327             "",
328             "    @Provides @SomeQualifier",
329             "    static String provideSomeQualifiedString() { return \"\"; }",
330             "  }",
331             "",
332             "  @Module",
333             "  static class TestModule2 {",
334             "    @Provides Map<String, String> stringMap() {",
335             "      return new HashMap<String, String>();",
336             "    }",
337             "  }",
338             "",
339             "  @Module(includes = { TestModule1.class, TestModule2.class})",
340             "  abstract static class TestModule3 {}",
341             "",
342             "  @Component(modules = { TestModule1.class, TestModule2.class })",
343             "  interface TestComponent {",
344             "    Map<String, String> getStringMap();",
345             "  }",
346             "}");
347 
348     Compilation compilation =
349         compilerWithOptions(
350                 fullBindingGraphValidationOption())
351             .compile(component);
352     assertThat(compilation).failed();
353     assertThat(compilation)
354         .hadErrorContaining(
355             message(
356                 "Map<String,String> has incompatible bindings "
357                     + "or declarations:",
358                 "    Map bindings and declarations:",
359                 "        @Binds @IntoMap @StringKey(\"bar\") String"
360                     + " Outer.TestModule1.bindStringMapEntry(@Outer.SomeQualifier "
361                     + "String)",
362                 "        @Provides @IntoMap @StringKey(\"foo\") String"
363                     + " Outer.TestModule1.stringMapEntry()",
364                 "    Unique bindings and declarations:",
365                 "        @Provides Map<String,String> Outer.TestModule2.stringMap()"))
366         .inFile(component)
367         .onLineContaining(
368             fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
369   }
370 
371   @Test
duplicateExplicitBindings_UniqueBindingAndMultibindingDeclaration_Set()372   public void duplicateExplicitBindings_UniqueBindingAndMultibindingDeclaration_Set() {
373     JavaFileObject component =
374         JavaFileObjects.forSourceLines(
375             "test.Outer",
376             "package test;",
377             "",
378             "import dagger.Component;",
379             "import dagger.Module;",
380             "import dagger.Provides;",
381             "import dagger.multibindings.Multibinds;",
382             "import java.util.HashSet;",
383             "import java.util.Set;",
384             "",
385             "final class Outer {",
386             "  @Module",
387             "  abstract static class TestModule1 {",
388             "    @Multibinds abstract Set<String> stringSet();",
389             "  }",
390             "",
391             "  @Module",
392             "  static class TestModule2 {",
393             "    @Provides Set<String> stringSet() { return new HashSet<String>(); }",
394             "  }",
395             "",
396             "  @Module(includes = { TestModule1.class, TestModule2.class})",
397             "  abstract static class TestModule3 {}",
398             "",
399             "  @Component(modules = { TestModule1.class, TestModule2.class })",
400             "  interface TestComponent {",
401             "    Set<String> getStringSet();",
402             "  }",
403             "}");
404 
405     Compilation compilation =
406         compilerWithOptions(
407                 fullBindingGraphValidationOption())
408             .compile(component);
409     assertThat(compilation).failed();
410     assertThat(compilation)
411         .hadErrorContaining(
412             message(
413                 "Set<String> has incompatible bindings or declarations:",
414                 "    Set bindings and declarations:",
415                 "        @Multibinds Set<String> "
416                     + "Outer.TestModule1.stringSet()",
417                 "    Unique bindings and declarations:",
418                 "        @Provides Set<String> Outer.TestModule2.stringSet()"))
419         .inFile(component)
420         .onLineContaining(
421             fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
422   }
423 
424   @Test
duplicateExplicitBindings_UniqueBindingAndMultibindingDeclaration_Map()425   public void duplicateExplicitBindings_UniqueBindingAndMultibindingDeclaration_Map() {
426     JavaFileObject component =
427         JavaFileObjects.forSourceLines(
428             "test.Outer",
429             "package test;",
430             "",
431             "import dagger.Component;",
432             "import dagger.Module;",
433             "import dagger.Provides;",
434             "import dagger.multibindings.Multibinds;",
435             "import java.util.HashMap;",
436             "import java.util.Map;",
437             "",
438             "final class Outer {",
439             "  @Module",
440             "  abstract static class TestModule1 {",
441             "    @Multibinds abstract Map<String, String> stringMap();",
442             "  }",
443             "",
444             "  @Module",
445             "  static class TestModule2 {",
446             "    @Provides Map<String, String> stringMap() {",
447             "      return new HashMap<String, String>();",
448             "    }",
449             "  }",
450             "",
451             "  @Module(includes = { TestModule1.class, TestModule2.class})",
452             "  abstract static class TestModule3 {}",
453             "",
454             "  @Component(modules = { TestModule1.class, TestModule2.class })",
455             "  interface TestComponent {",
456             "    Map<String, String> getStringMap();",
457             "  }",
458             "}");
459 
460     Compilation compilation =
461         compilerWithOptions(
462                 fullBindingGraphValidationOption())
463             .compile(component);
464     assertThat(compilation).failed();
465     assertThat(compilation)
466         .hadErrorContaining(
467             message(
468                 "Map<String,String> has incompatible bindings "
469                     + "or declarations:",
470                 "    Map bindings and declarations:",
471                 "        @Multibinds Map<String,String> "
472                     + "Outer.TestModule1.stringMap()",
473                 "    Unique bindings and declarations:",
474                 "        @Provides Map<String,String> Outer.TestModule2.stringMap()"))
475         .inFile(component)
476         .onLineContaining(
477             fullBindingGraphValidation ? "class TestModule3" : "interface TestComponent");
478   }
479 
duplicateBindings_TruncateAfterLimit()480   @Test public void duplicateBindings_TruncateAfterLimit() {
481     JavaFileObject component =
482         JavaFileObjects.forSourceLines(
483             "test.Outer",
484             "package test;",
485             "",
486             "import dagger.Component;",
487             "import dagger.Module;",
488             "import dagger.Provides;",
489             "import javax.inject.Inject;",
490             "",
491             "final class Outer {",
492             "  interface A {}",
493             "",
494             "  @Module",
495             "  static class Module01 {",
496             "    @Provides A provideA() { return new A() {}; }",
497             "  }",
498             "",
499             "  @Module",
500             "  static class Module02 {",
501             "    @Provides A provideA() { return new A() {}; }",
502             "  }",
503             "",
504             "  @Module",
505             "  static class Module03 {",
506             "    @Provides A provideA() { return new A() {}; }",
507             "  }",
508             "",
509             "  @Module",
510             "  static class Module04 {",
511             "    @Provides A provideA() { return new A() {}; }",
512             "  }",
513             "",
514             "  @Module",
515             "  static class Module05 {",
516             "    @Provides A provideA() { return new A() {}; }",
517             "  }",
518             "",
519             "  @Module",
520             "  static class Module06 {",
521             "    @Provides A provideA() { return new A() {}; }",
522             "  }",
523             "",
524             "  @Module",
525             "  static class Module07 {",
526             "    @Provides A provideA() { return new A() {}; }",
527             "  }",
528             "",
529             "  @Module",
530             "  static class Module08 {",
531             "    @Provides A provideA() { return new A() {}; }",
532             "  }",
533             "",
534             "  @Module",
535             "  static class Module09 {",
536             "    @Provides A provideA() { return new A() {}; }",
537             "  }",
538             "",
539             "  @Module",
540             "  static class Module10 {",
541             "    @Provides A provideA() { return new A() {}; }",
542             "  }",
543             "",
544             "  @Module",
545             "  static class Module11 {",
546             "    @Provides A provideA() { return new A() {}; }",
547             "  }",
548             "",
549             "  @Module",
550             "  static class Module12 {",
551             "    @Provides A provideA() { return new A() {}; }",
552             "  }",
553             "",
554             "  @Module(includes = {",
555             "    Module01.class,",
556             "    Module02.class,",
557             "    Module03.class,",
558             "    Module04.class,",
559             "    Module05.class,",
560             "    Module06.class,",
561             "    Module07.class,",
562             "    Module08.class,",
563             "    Module09.class,",
564             "    Module10.class,",
565             "    Module11.class,",
566             "    Module12.class",
567             "  })",
568             "  abstract static class Modules {}",
569             "",
570             "  @Component(modules = {",
571             "    Module01.class,",
572             "    Module02.class,",
573             "    Module03.class,",
574             "    Module04.class,",
575             "    Module05.class,",
576             "    Module06.class,",
577             "    Module07.class,",
578             "    Module08.class,",
579             "    Module09.class,",
580             "    Module10.class,",
581             "    Module11.class,",
582             "    Module12.class",
583             "  })",
584             "  interface TestComponent {",
585             "    A getA();",
586             "  }",
587             "}");
588 
589     Compilation compilation =
590         compilerWithOptions(
591                 fullBindingGraphValidationOption())
592             .compile(component);
593     assertThat(compilation).failed();
594     assertThat(compilation)
595         .hadErrorContaining(
596             message(
597                 "Outer.A is bound multiple times:",
598                 "    @Provides Outer.A Outer.Module01.provideA()",
599                 "    @Provides Outer.A Outer.Module02.provideA()",
600                 "    @Provides Outer.A Outer.Module03.provideA()",
601                 "    @Provides Outer.A Outer.Module04.provideA()",
602                 "    @Provides Outer.A Outer.Module05.provideA()",
603                 "    @Provides Outer.A Outer.Module06.provideA()",
604                 "    @Provides Outer.A Outer.Module07.provideA()",
605                 "    @Provides Outer.A Outer.Module08.provideA()",
606                 "    @Provides Outer.A Outer.Module09.provideA()",
607                 "    @Provides Outer.A Outer.Module10.provideA()",
608                 "    and 2 others"))
609         .inFile(component)
610         .onLineContaining(fullBindingGraphValidation ? "class Modules" : "interface TestComponent");
611   }
612 
613   @Test
childBindingConflictsWithParent()614   public void childBindingConflictsWithParent() {
615     JavaFileObject aComponent =
616         JavaFileObjects.forSourceLines(
617             "test.A",
618             "package test;",
619             "",
620             "import dagger.Component;",
621             "import dagger.Module;",
622             "import dagger.Provides;",
623             "",
624             "@Component(modules = A.AModule.class)",
625             "interface A {",
626             "  Object conflict();",
627             "",
628             "  B.Builder b();",
629             "",
630             "  @Module(subcomponents = B.class)",
631             "  static class AModule {",
632             "    @Provides static Object abConflict() {",
633             "      return \"a\";",
634             "    }",
635             "  }",
636             "}");
637     JavaFileObject bComponent =
638         JavaFileObjects.forSourceLines(
639             "test.B",
640             "package test;",
641             "",
642             "import dagger.Module;",
643             "import dagger.Provides;",
644             "import dagger.Subcomponent;",
645             "",
646             "@Subcomponent(modules = B.BModule.class)",
647             "interface B {",
648             "  Object conflict();",
649             "",
650             "  @Subcomponent.Builder",
651             "  interface Builder {",
652             "    B build();",
653             "  }",
654             "",
655             "  @Module",
656             "  static class BModule {",
657             "    @Provides static Object abConflict() {",
658             "      return \"b\";",
659             "    }",
660             "  }",
661             "}");
662 
663     Compilation compilation =
664         compilerWithOptions(
665                 fullBindingGraphValidationOption())
666             .compile(aComponent, bComponent);
667     assertThat(compilation).failed();
668     assertThat(compilation)
669         .hadErrorContaining(
670             message(
671                 "Object is bound multiple times:",
672                 "    @Provides Object test.A.AModule.abConflict()",
673                 "    @Provides Object test.B.BModule.abConflict()"))
674         .inFile(aComponent)
675         .onLineContaining(fullBindingGraphValidation ? "class AModule" : "interface A {");
676   }
677 
678   @Test
grandchildBindingConflictsWithGrandparent()679   public void grandchildBindingConflictsWithGrandparent() {
680     JavaFileObject aComponent =
681         JavaFileObjects.forSourceLines(
682             "test.A",
683             "package test;",
684             "",
685             "import dagger.Component;",
686             "import dagger.Module;",
687             "import dagger.Provides;",
688             "",
689             "@Component(modules = A.AModule.class)",
690             "interface A {",
691             "  Object conflict();",
692             "",
693             "  B.Builder b();",
694             "",
695             "  @Module(subcomponents = B.class)",
696             "  static class AModule {",
697             "    @Provides static Object acConflict() {",
698             "      return \"a\";",
699             "    }",
700             "  }",
701             "}");
702     JavaFileObject bComponent =
703         JavaFileObjects.forSourceLines(
704             "test.B",
705             "package test;",
706             "",
707             "import dagger.Subcomponent;",
708             "",
709             "@Subcomponent",
710             "interface B {",
711             "  C.Builder c();",
712             "",
713             "  @Subcomponent.Builder",
714             "  interface Builder {",
715             "    B build();",
716             "  }",
717             "}");
718     JavaFileObject cComponent =
719         JavaFileObjects.forSourceLines(
720             "test.C",
721             "package test;",
722             "",
723             "import dagger.Module;",
724             "import dagger.Provides;",
725             "import dagger.Subcomponent;",
726             "",
727             "@Subcomponent(modules = C.CModule.class)",
728             "interface C {",
729             "  Object conflict();",
730             "",
731             "  @Subcomponent.Builder",
732             "  interface Builder {",
733             "    C build();",
734             "  }",
735             "",
736             "  @Module",
737             "  static class CModule {",
738             "    @Provides static Object acConflict() {",
739             "      return \"c\";",
740             "    }",
741             "  }",
742             "}");
743 
744     Compilation compilation =
745         compilerWithOptions(
746                 fullBindingGraphValidationOption())
747             .compile(aComponent, bComponent, cComponent);
748     assertThat(compilation).failed();
749     assertThat(compilation)
750         .hadErrorContaining(
751             message(
752                 "Object is bound multiple times:",
753                 "    @Provides Object test.A.AModule.acConflict()",
754                 "    @Provides Object test.C.CModule.acConflict()"))
755         .inFile(aComponent)
756         .onLineContaining(fullBindingGraphValidation ? "class AModule" : "interface A {");
757   }
758 
759   @Test
grandchildBindingConflictsWithChild()760   public void grandchildBindingConflictsWithChild() {
761     JavaFileObject aComponent =
762         JavaFileObjects.forSourceLines(
763             "test.A",
764             "package test;",
765             "",
766             "import dagger.Component;",
767             "",
768             "@Component",
769             "interface A {",
770             "  B b();",
771             "}");
772     JavaFileObject bComponent =
773         JavaFileObjects.forSourceLines(
774             "test.B",
775             "package test;",
776             "",
777             "import dagger.Module;",
778             "import dagger.Provides;",
779             "import dagger.Subcomponent;",
780             "",
781             "@Subcomponent(modules = B.BModule.class)",
782             "interface B {",
783             "  Object conflict();",
784             "",
785             "  C.Builder c();",
786             "",
787             "  @Module(subcomponents = C.class)",
788             "  static class BModule {",
789             "    @Provides static Object bcConflict() {",
790             "      return \"b\";",
791             "    }",
792             "  }",
793             "}");
794     JavaFileObject cComponent =
795         JavaFileObjects.forSourceLines(
796             "test.C",
797             "package test;",
798             "",
799             "import dagger.Module;",
800             "import dagger.Provides;",
801             "import dagger.Subcomponent;",
802             "",
803             "@Subcomponent(modules = C.CModule.class)",
804             "interface C {",
805             "  Object conflict();",
806             "",
807             "  @Subcomponent.Builder",
808             "  interface Builder {",
809             "    C build();",
810             "  }",
811             "",
812             "  @Module",
813             "  static class CModule {",
814             "    @Provides static Object bcConflict() {",
815             "      return \"c\";",
816             "    }",
817             "  }",
818             "}");
819 
820     Compilation compilation =
821         compilerWithOptions(
822                 fullBindingGraphValidationOption())
823             .compile(aComponent, bComponent, cComponent);
824     assertThat(compilation).failed();
825     assertThat(compilation)
826         .hadErrorContaining(
827             message(
828                 "Object is bound multiple times:",
829                 "    @Provides Object test.B.BModule.bcConflict()",
830                 "    @Provides Object test.C.CModule.bcConflict()"))
831         .inFile(fullBindingGraphValidation ? bComponent : aComponent)
832         .onLineContaining(fullBindingGraphValidation ? "class BModule" : "interface A {");
833   }
834 
835   @Test
childProvidesConflictsWithParentInjects()836   public void childProvidesConflictsWithParentInjects() {
837     assumeFalse(fullBindingGraphValidation);
838 
839     JavaFileObject foo =
840         JavaFileObjects.forSourceLines(
841             "test.Foo",
842             "package test;",
843             "",
844             "import java.util.Set;",
845             "import javax.inject.Inject;",
846             "",
847             "final class Foo {",
848             "  @Inject Foo(Set<String> strings) {}",
849             "}");
850     JavaFileObject injected1 =
851         JavaFileObjects.forSourceLines(
852             "test.Injected1",
853             "package test;",
854             "",
855             "import dagger.Component;",
856             "import dagger.Module;",
857             "import dagger.Provides;",
858             "import dagger.multibindings.IntoSet;",
859             "import java.util.Set;",
860             "",
861             "@Component(modules = Injected1.Injected1Module.class)",
862             "interface Injected1 {",
863             "  Foo foo();",
864             "  Injected2 injected2();",
865             "",
866             "  @Module",
867             "  interface Injected1Module {",
868             "    @Provides @IntoSet static String string() {",
869             "      return \"injected1\";",
870             "    }",
871             "  }",
872             "}");
873     JavaFileObject injected2 =
874         JavaFileObjects.forSourceLines(
875             "test.Injected2",
876             "package test;",
877             "",
878             "import dagger.Module;",
879             "import dagger.Provides;",
880             "import dagger.Subcomponent;",
881             "import dagger.multibindings.IntoSet;",
882             "import java.util.Set;",
883             "",
884             "@Subcomponent(modules = Injected2.Injected2Module.class)",
885             "interface Injected2 {",
886             "  Foo foo();",
887             "  Provided1 provided1();",
888             "",
889             "  @Module",
890             "  interface Injected2Module {",
891             "    @Provides @IntoSet static String string() {",
892             "      return \"injected2\";",
893             "    }",
894             "  }",
895             "}");
896     JavaFileObject provided1 =
897         JavaFileObjects.forSourceLines(
898             "test.Provided1",
899             "package test;",
900             "",
901             "import dagger.Module;",
902             "import dagger.Provides;",
903             "import dagger.Subcomponent;",
904             "import dagger.multibindings.IntoSet;",
905             "import java.util.Set;",
906             "",
907             "@Subcomponent(modules = Provided1.Provided1Module.class)",
908             "interface Provided1 {",
909             "  Foo foo();",
910             "  Provided2 provided2();",
911             "",
912             "  @Module",
913             "  static class Provided1Module {",
914             "    @Provides static Foo provideFoo(Set<String> strings) {",
915             "      return new Foo(strings);",
916             "    }",
917             "",
918             "    @Provides @IntoSet static String string() {",
919             "      return \"provided1\";",
920             "    }",
921             "  }",
922             "}");
923     JavaFileObject provided2 =
924         JavaFileObjects.forSourceLines(
925             "test.Provided2",
926             "package test;",
927             "",
928             "import dagger.Module;",
929             "import dagger.Provides;",
930             "import dagger.Subcomponent;",
931             "import dagger.multibindings.IntoSet;",
932             "",
933             "@Subcomponent(modules = Provided2.Provided2Module.class)",
934             "interface Provided2 {",
935             "  Foo foo();",
936             "",
937             "  @Module",
938             "  static class Provided2Module {",
939             "    @Provides @IntoSet static String string() {",
940             "      return \"provided2\";",
941             "    }",
942             "  }",
943             "}");
944 
945     Compilation compilation =
946         daggerCompiler().compile(foo, injected1, injected2, provided1, provided2);
947     assertThat(compilation).succeeded();
948     assertThat(compilation)
949         .hadWarningContaining(
950             message(
951                 "Foo is bound multiple times:",
952                 "    @Inject Foo(Set<String>) [Injected1]",
953                 "    @Provides Foo Provided1.Provided1Module.provideFoo(Set<String>) "
954                     + "[Injected1 → Injected2 → Provided1]"))
955         .inFile(injected1)
956         .onLineContaining("interface Injected1 {");
957   }
958 
959   @Test
grandchildBindingConflictsWithParentWithNullableViolationAsWarning()960   public void grandchildBindingConflictsWithParentWithNullableViolationAsWarning() {
961     JavaFileObject parentConflictsWithChild =
962         JavaFileObjects.forSourceLines(
963             "test.ParentConflictsWithChild",
964             "package test;",
965             "",
966             "import dagger.Component;",
967             "import dagger.Module;",
968             "import dagger.Provides;",
969             "import javax.annotation.Nullable;",
970             "",
971             "@Component(modules = ParentConflictsWithChild.ParentModule.class)",
972             "interface ParentConflictsWithChild {",
973             "  Child.Builder child();",
974             "",
975             "  @Module(subcomponents = Child.class)",
976             "  static class ParentModule {",
977             "    @Provides @Nullable static Object nullableParentChildConflict() {",
978             "      return \"parent\";",
979             "    }",
980             "  }",
981             "}");
982     JavaFileObject child =
983         JavaFileObjects.forSourceLines(
984             "test.Child",
985             "package test;",
986             "",
987             "import dagger.Module;",
988             "import dagger.Provides;",
989             "import dagger.Subcomponent;",
990             "",
991             "@Subcomponent(modules = Child.ChildModule.class)",
992             "interface Child {",
993             "  Object parentChildConflictThatViolatesNullability();",
994             "",
995             "  @Subcomponent.Builder",
996             "  interface Builder {",
997             "    Child build();",
998             "  }",
999             "",
1000             "  @Module",
1001             "  static class ChildModule {",
1002             "    @Provides static Object nonNullableParentChildConflict() {",
1003             "      return \"child\";",
1004             "    }",
1005             "  }",
1006             "}");
1007 
1008     Compilation compilation =
1009         compilerWithOptions(
1010                 "-Adagger.nullableValidation=WARNING",
1011                 fullBindingGraphValidationOption())
1012             .compile(parentConflictsWithChild, child);
1013     assertThat(compilation).failed();
1014     assertThat(compilation)
1015         .hadErrorContaining(
1016             message(
1017                 "Object is bound multiple times:",
1018                 "    @Provides Object Child.ChildModule.nonNullableParentChildConflict()",
1019                 "    @Provides @Nullable Object"
1020                     + " ParentConflictsWithChild.ParentModule.nullableParentChildConflict()"))
1021         .inFile(parentConflictsWithChild)
1022         .onLineContaining(
1023             fullBindingGraphValidation
1024                 ? "class ParentModule"
1025                 : "interface ParentConflictsWithChild");
1026   }
1027 
fullBindingGraphValidationOption()1028   private String fullBindingGraphValidationOption() {
1029     return "-Adagger.fullBindingGraphValidation=" + (fullBindingGraphValidation ? "ERROR" : "NONE");
1030   }
1031 
1032   @Test
reportedInParentAndChild()1033   public void reportedInParentAndChild() {
1034     JavaFileObject parent =
1035         JavaFileObjects.forSourceLines(
1036             "test.Parent",
1037             "package test;",
1038             "",
1039             "import dagger.Component;",
1040             "",
1041             "@Component(modules = ParentModule.class)",
1042             "interface Parent {",
1043             "  Child.Builder childBuilder();",
1044             "  String duplicated();",
1045             "}");
1046     JavaFileObject parentModule =
1047         JavaFileObjects.forSourceLines(
1048             "test.ParentModule",
1049             "package test;",
1050             "",
1051             "import dagger.BindsOptionalOf;",
1052             "import dagger.Module;",
1053             "import dagger.Provides;",
1054             "import java.util.Optional;",
1055             "",
1056             "@Module",
1057             "interface ParentModule {",
1058             "  @Provides static String one(Optional<Object> optional) { return \"one\"; }",
1059             "  @Provides static String two() { return \"two\"; }",
1060             "  @BindsOptionalOf Object optional();",
1061             "}");
1062     JavaFileObject child =
1063         JavaFileObjects.forSourceLines(
1064             "test.Child",
1065             "package test;",
1066             "",
1067             "import dagger.Subcomponent;",
1068             "",
1069             "@Subcomponent(modules = ChildModule.class)",
1070             "interface Child {",
1071             "  String duplicated();",
1072             "",
1073             "  @Subcomponent.Builder",
1074             "  interface Builder {",
1075             "    Child build();",
1076             "  }",
1077             "}");
1078     JavaFileObject childModule =
1079         JavaFileObjects.forSourceLines(
1080             "test.ChildModule",
1081             "package test;",
1082             "",
1083             "import dagger.Module;",
1084             "import dagger.Provides;",
1085             "import java.util.Optional;",
1086             "",
1087             "@Module",
1088             "interface ChildModule {",
1089             "  @Provides static Object object() { return \"object\"; }",
1090             "}");
1091     Compilation compilation = daggerCompiler().compile(parent, parentModule, child, childModule);
1092     assertThat(compilation).failed();
1093     assertThat(compilation)
1094         .hadErrorContaining("String is bound multiple times")
1095         .inFile(parent)
1096         .onLineContaining("interface Parent");
1097     assertThat(compilation).hadErrorCount(1);
1098   }
1099 }
1100