xref: /aosp_15_r20/external/dagger2/javatests/dagger/internal/codegen/DuplicateBindingsValidationTest.java (revision f585d8a307d0621d6060bd7e80091fdcbf94fe27)
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 dagger.internal.codegen.TestUtils.message;
20 import static org.junit.Assume.assumeFalse;
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 org.junit.Test;
28 import org.junit.runner.RunWith;
29 import org.junit.runners.Parameterized;
30 import org.junit.runners.Parameterized.Parameters;
31 
32 @RunWith(Parameterized.class)
33 public class DuplicateBindingsValidationTest {
34 
35   @Parameters(name = "fullBindingGraphValidation={0}")
parameters()36   public static ImmutableList<Object[]> parameters() {
37     return ImmutableList.copyOf(new Object[][] {{false}, {true}});
38   }
39 
40   private final boolean fullBindingGraphValidation;
41 
DuplicateBindingsValidationTest(boolean fullBindingGraphValidation)42   public DuplicateBindingsValidationTest(boolean fullBindingGraphValidation) {
43     this.fullBindingGraphValidation = fullBindingGraphValidation;
44   }
45 
duplicateExplicitBindings_ProvidesAndComponentProvision()46   @Test public void duplicateExplicitBindings_ProvidesAndComponentProvision() {
47     assumeFalse(fullBindingGraphValidation);
48 
49     Source component =
50         CompilerTests.javaSource(
51             "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     CompilerTests.daggerCompiler(component)
86         .withProcessingOptions(
87             ImmutableMap.<String, String>builder()
88                 .putAll(fullBindingGraphValidationOption())
89                 .buildOrThrow())
90         .compile(
91             subject -> {
92               subject.hasErrorCount(1);
93               subject.hasErrorContaining(
94                       message(
95                           "Outer.A is bound multiple times:",
96                           "    @Provides Outer.A Outer.AModule.provideA(String)",
97                           "    Outer.A Outer.Parent.getA()"))
98                   .onSource(component)
99                   .onLineContaining("interface Child");
100             });
101   }
102 
duplicateExplicitBindings_TwoProvidesMethods()103   @Test public void duplicateExplicitBindings_TwoProvidesMethods() {
104     Source component =
105         CompilerTests.javaSource(
106             "test.Outer",
107             "package test;",
108             "",
109             "import dagger.Component;",
110             "import dagger.Module;",
111             "import dagger.Provides;",
112             "import javax.inject.Inject;",
113             "",
114             "final class Outer {",
115             "  interface A {}",
116             "",
117             "  static class B {",
118             "    @Inject B(A a) {}",
119             "  }",
120             "",
121             "  @Module",
122             "  static class Module1 {",
123             "    @Provides A provideA1() { return new A() {}; }",
124             "  }",
125             "",
126             "  @Module",
127             "  static class Module2 {",
128             "    @Provides String provideString() { return \"\"; }",
129             "    @Provides A provideA2(String s) { return new A() {}; }",
130             "  }",
131             "",
132             "  @Module(includes = { Module1.class, Module2.class})",
133             "  abstract static class Module3 {}",
134             "",
135             "  @Component(modules = { Module1.class, Module2.class})",
136             "  interface TestComponent {",
137             "    A getA();",
138             "    B getB();",
139             "  }",
140             "}");
141 
142     CompilerTests.daggerCompiler(component)
143         .withProcessingOptions(
144             ImmutableMap.<String, String>builder()
145                 .putAll(fullBindingGraphValidationOption())
146                 .buildOrThrow())
147         .compile(
148             subject -> {
149               // The duplicate bindngs are also requested from B, but we don't want to report them
150               // again.
151               subject.hasErrorCount(fullBindingGraphValidation ? 2 : 1);
152 
153               subject.hasErrorContaining(
154                       message(
155                           "Outer.A is bound multiple times:",
156                           "    @Provides Outer.A Outer.Module1.provideA1()",
157                           "    @Provides Outer.A Outer.Module2.provideA2(String)"))
158                   .onSource(component)
159                   .onLineContaining("interface TestComponent");
160 
161               if (fullBindingGraphValidation) {
162                 subject.hasErrorContaining(
163                         message(
164                             "Outer.A is bound multiple times:",
165                             "    @Provides Outer.A Outer.Module1.provideA1()",
166                             "    @Provides Outer.A Outer.Module2.provideA2(String)"))
167                     .onSource(component)
168                     .onLineContaining("class Module3");
169               }
170             });
171   }
172 
173   @Test
duplicateExplicitBindings_ProvidesVsBinds()174   public void duplicateExplicitBindings_ProvidesVsBinds() {
175     Source component =
176         CompilerTests.javaSource(
177             "test.Outer",
178             "package test;",
179             "",
180             "import dagger.Binds;",
181             "import dagger.Component;",
182             "import dagger.Module;",
183             "import dagger.Provides;",
184             "import javax.inject.Inject;",
185             "",
186             "final class Outer {",
187             "  interface A {}",
188             "",
189             "  static final class B implements A {",
190             "    @Inject B() {}",
191             "  }",
192             "",
193             "  @Module",
194             "  static class Module1 {",
195             "    @Provides A provideA1() { return new A() {}; }",
196             "  }",
197             "",
198             "  @Module",
199             "  static abstract class Module2 {",
200             "    @Binds abstract A bindA2(B b);",
201             "  }",
202             "",
203             "  @Module(includes = { Module1.class, Module2.class})",
204             "  abstract static class Module3 {}",
205             "",
206             "  @Component(modules = { Module1.class, Module2.class})",
207             "  interface TestComponent {",
208             "    A getA();",
209             "  }",
210             "}");
211 
212     CompilerTests.daggerCompiler(component)
213         .withProcessingOptions(
214             ImmutableMap.<String, String>builder()
215                 .putAll(fullBindingGraphValidationOption())
216                 .buildOrThrow())
217         .compile(
218             subject -> {
219               String errorMessage =
220                   message(
221                       "Outer.A is bound multiple times:",
222                       "    @Provides Outer.A Outer.Module1.provideA1()",
223                       "    @Binds Outer.A Outer.Module2.bindA2(Outer.B)");
224               if (fullBindingGraphValidation) {
225                 subject.hasErrorCount(2);
226                 subject.hasErrorContaining(errorMessage)
227                     .onSource(component)
228                     .onLineContaining("class Module3");
229                 subject.hasErrorContaining(errorMessage)
230                     .onSource(component)
231                     .onLineContaining("interface TestComponent");
232               } else {
233                 subject.hasErrorCount(1);
234                 subject.hasErrorContaining(errorMessage)
235                     .onSource(component)
236                     .onLineContaining("interface TestComponent");
237               }
238             });
239   }
240 
241   @Test
duplicateExplicitBindings_multibindingsAndExplicitSets()242   public void duplicateExplicitBindings_multibindingsAndExplicitSets() {
243     Source component =
244         CompilerTests.javaSource(
245             "test.Outer",
246             "package test;",
247             "",
248             "import dagger.Binds;",
249             "import dagger.Component;",
250             "import dagger.Module;",
251             "import dagger.Provides;",
252             "import dagger.multibindings.IntoSet;",
253             "import java.util.HashSet;",
254             "import java.util.Set;",
255             "import javax.inject.Qualifier;",
256             "",
257             "final class Outer {",
258             "  @Qualifier @interface SomeQualifier {}",
259             "",
260             "  @Module",
261             "  abstract static class TestModule1 {",
262             "    @Provides @IntoSet static String stringSetElement() { return \"\"; }",
263             "",
264             "    @Binds",
265             "    @IntoSet abstract String bindStringSetElement(@SomeQualifier String value);",
266             "",
267             "    @Provides @SomeQualifier",
268             "    static String provideSomeQualifiedString() { return \"\"; }",
269             "  }",
270             "",
271             "  @Module",
272             "  static class TestModule2 {",
273             "    @Provides Set<String> stringSet() { return new HashSet<String>(); }",
274             "  }",
275             "",
276             "  @Module(includes = { TestModule1.class, TestModule2.class})",
277             "  abstract static class TestModule3 {}",
278             "",
279             "  @Component(modules = { TestModule1.class, TestModule2.class })",
280             "  interface TestComponent {",
281             "    Set<String> getStringSet();",
282             "  }",
283             "}");
284 
285     CompilerTests.daggerCompiler(component)
286         .withProcessingOptions(
287             ImmutableMap.<String, String>builder()
288                 .putAll(fullBindingGraphValidationOption())
289                 .buildOrThrow())
290         .compile(
291             subject -> {
292               String errorMessage =
293                   message(
294                       "Set<String> has incompatible bindings or declarations:",
295                       "    Set bindings and declarations:",
296                       "        @Binds @IntoSet String "
297                           + "Outer.TestModule1.bindStringSetElement(@Outer.SomeQualifier String)",
298                       "        @Provides @IntoSet String "
299                           + "Outer.TestModule1.stringSetElement()",
300                       "    Unique bindings and declarations:",
301                       "        @Provides Set<String> Outer.TestModule2.stringSet()");
302               if (fullBindingGraphValidation) {
303                 subject.hasErrorCount(2);
304                 subject.hasErrorContaining(errorMessage)
305                     .onSource(component)
306                     .onLineContaining("class TestModule3");
307                 subject.hasErrorContaining(errorMessage)
308                     .onSource(component)
309                     .onLineContaining("interface TestComponent");
310               } else {
311                 subject.hasErrorCount(1);
312                 subject.hasErrorContaining(errorMessage)
313                     .onSource(component)
314                     .onLineContaining("interface TestComponent");
315               }
316             });
317   }
318 
319   @Test
duplicateExplicitBindings_multibindingsAndExplicitMaps()320   public void duplicateExplicitBindings_multibindingsAndExplicitMaps() {
321     Source component =
322         CompilerTests.javaSource(
323             "test.Outer",
324             "package test;",
325             "",
326             "import dagger.Binds;",
327             "import dagger.Component;",
328             "import dagger.Module;",
329             "import dagger.Provides;",
330             "import dagger.multibindings.IntoMap;",
331             "import dagger.multibindings.StringKey;",
332             "import java.util.HashMap;",
333             "import java.util.Map;",
334             "import javax.inject.Qualifier;",
335             "",
336             "final class Outer {",
337             "  @Qualifier @interface SomeQualifier {}",
338             "",
339             "  @Module",
340             "  abstract static class TestModule1 {",
341             "    @Provides @IntoMap",
342             "    @StringKey(\"foo\")",
343             "    static String stringMapEntry() { return \"\"; }",
344             "",
345             "    @Binds @IntoMap @StringKey(\"bar\")",
346             "    abstract String bindStringMapEntry(@SomeQualifier String value);",
347             "",
348             "    @Provides @SomeQualifier",
349             "    static String provideSomeQualifiedString() { return \"\"; }",
350             "  }",
351             "",
352             "  @Module",
353             "  static class TestModule2 {",
354             "    @Provides Map<String, String> stringMap() {",
355             "      return new HashMap<String, String>();",
356             "    }",
357             "  }",
358             "",
359             "  @Module(includes = { TestModule1.class, TestModule2.class})",
360             "  abstract static class TestModule3 {}",
361             "",
362             "  @Component(modules = { TestModule1.class, TestModule2.class })",
363             "  interface TestComponent {",
364             "    Map<String, String> getStringMap();",
365             "  }",
366             "}");
367 
368     CompilerTests.daggerCompiler(component)
369         .withProcessingOptions(
370             ImmutableMap.<String, String>builder()
371                 .putAll(fullBindingGraphValidationOption())
372                 .buildOrThrow())
373         .compile(
374             subject -> {
375               String errorMessage =
376                   message(
377                       "Map<String,String> has incompatible bindings or declarations:",
378                       "    Map bindings and declarations:",
379                       "        @Binds @IntoMap @StringKey(\"bar\") String"
380                           + " Outer.TestModule1.bindStringMapEntry(@Outer.SomeQualifier String)",
381                       "        @Provides @IntoMap @StringKey(\"foo\") String"
382                           + " Outer.TestModule1.stringMapEntry()",
383                       "    Unique bindings and declarations:",
384                       "        @Provides Map<String,String> Outer.TestModule2.stringMap()");
385               if (fullBindingGraphValidation) {
386                 subject.hasErrorCount(2);
387                 subject.hasErrorContaining(errorMessage)
388                     .onSource(component)
389                     .onLineContaining("class TestModule3");
390                 subject.hasErrorContaining(errorMessage)
391                     .onSource(component)
392                     .onLineContaining("interface TestComponent");
393               } else {
394                 subject.hasErrorCount(1);
395                 subject.hasErrorContaining(errorMessage)
396                     .onSource(component)
397                     .onLineContaining("interface TestComponent");
398               }
399             });
400   }
401 
402   @Test
duplicateExplicitBindings_UniqueBindingAndMultibindingDeclaration_Set()403   public void duplicateExplicitBindings_UniqueBindingAndMultibindingDeclaration_Set() {
404     Source component =
405         CompilerTests.javaSource(
406             "test.Outer",
407             "package test;",
408             "",
409             "import dagger.Component;",
410             "import dagger.Module;",
411             "import dagger.Provides;",
412             "import dagger.multibindings.Multibinds;",
413             "import java.util.HashSet;",
414             "import java.util.Set;",
415             "",
416             "final class Outer {",
417             "  @Module",
418             "  abstract static class TestModule1 {",
419             "    @Multibinds abstract Set<String> stringSet();",
420             "  }",
421             "",
422             "  @Module",
423             "  static class TestModule2 {",
424             "    @Provides Set<String> stringSet() { return new HashSet<String>(); }",
425             "  }",
426             "",
427             "  @Module(includes = { TestModule1.class, TestModule2.class})",
428             "  abstract static class TestModule3 {}",
429             "",
430             "  @Component(modules = { TestModule1.class, TestModule2.class })",
431             "  interface TestComponent {",
432             "    Set<String> getStringSet();",
433             "  }",
434             "}");
435 
436     CompilerTests.daggerCompiler(component)
437         .withProcessingOptions(
438             ImmutableMap.<String, String>builder()
439                 .putAll(fullBindingGraphValidationOption())
440                 .buildOrThrow())
441         .compile(
442             subject -> {
443               String errorMessage =
444                   message(
445                       "Set<String> has incompatible bindings or declarations:",
446                       "    Set bindings and declarations:",
447                       "        @Multibinds Set<String> Outer.TestModule1.stringSet()",
448                       "    Unique bindings and declarations:",
449                       "        @Provides Set<String> Outer.TestModule2.stringSet()");
450               if (fullBindingGraphValidation) {
451                 subject.hasErrorCount(2);
452                 subject.hasErrorContaining(errorMessage)
453                     .onSource(component)
454                     .onLineContaining("class TestModule3");
455                 subject.hasErrorContaining(errorMessage)
456                     .onSource(component)
457                     .onLineContaining("interface TestComponent");
458               } else {
459                 subject.hasErrorCount(1);
460                 subject.hasErrorContaining(errorMessage)
461                     .onSource(component)
462                     .onLineContaining("interface TestComponent");
463               }
464             });
465   }
466 
467   @Test
duplicateExplicitBindings_UniqueBindingAndMultibindingDeclaration_Map()468   public void duplicateExplicitBindings_UniqueBindingAndMultibindingDeclaration_Map() {
469     Source component =
470         CompilerTests.javaSource(
471             "test.Outer",
472             "package test;",
473             "",
474             "import dagger.Component;",
475             "import dagger.Module;",
476             "import dagger.Provides;",
477             "import dagger.multibindings.Multibinds;",
478             "import java.util.HashMap;",
479             "import java.util.Map;",
480             "",
481             "final class Outer {",
482             "  @Module",
483             "  abstract static class TestModule1 {",
484             "    @Multibinds abstract Map<String, String> stringMap();",
485             "  }",
486             "",
487             "  @Module",
488             "  static class TestModule2 {",
489             "    @Provides Map<String, String> stringMap() {",
490             "      return new HashMap<String, String>();",
491             "    }",
492             "  }",
493             "",
494             "  @Module(includes = { TestModule1.class, TestModule2.class})",
495             "  abstract static class TestModule3 {}",
496             "",
497             "  @Component(modules = { TestModule1.class, TestModule2.class })",
498             "  interface TestComponent {",
499             "    Map<String, String> getStringMap();",
500             "  }",
501             "}");
502 
503     CompilerTests.daggerCompiler(component)
504         .withProcessingOptions(
505             ImmutableMap.<String, String>builder()
506                 .putAll(fullBindingGraphValidationOption())
507                 .buildOrThrow())
508         .compile(
509             subject -> {
510               String errorMessage =
511                   message(
512                       "Map<String,String> has incompatible bindings or declarations:",
513                       "    Map bindings and declarations:",
514                       "        @Multibinds Map<String,String> Outer.TestModule1.stringMap()",
515                       "    Unique bindings and declarations:",
516                       "        @Provides Map<String,String> Outer.TestModule2.stringMap()");
517               if (fullBindingGraphValidation) {
518                 subject.hasErrorCount(2);
519                 subject.hasErrorContaining(errorMessage)
520                     .onSource(component)
521                     .onLineContaining("class TestModule3");
522                 subject.hasErrorContaining(errorMessage)
523                     .onSource(component)
524                     .onLineContaining("interface TestComponent");
525               } else {
526                 subject.hasErrorCount(1);
527                 subject.hasErrorContaining(errorMessage)
528                     .onSource(component)
529                     .onLineContaining("interface TestComponent");
530               }
531             });
532   }
533 
duplicateBindings_TruncateAfterLimit()534   @Test public void duplicateBindings_TruncateAfterLimit() {
535     Source component =
536         CompilerTests.javaSource(
537             "test.Outer",
538             "package test;",
539             "",
540             "import dagger.Component;",
541             "import dagger.Module;",
542             "import dagger.Provides;",
543             "import javax.inject.Inject;",
544             "",
545             "final class Outer {",
546             "  interface A {}",
547             "",
548             "  @Module",
549             "  static class Module01 {",
550             "    @Provides A provideA() { return new A() {}; }",
551             "  }",
552             "",
553             "  @Module",
554             "  static class Module02 {",
555             "    @Provides A provideA() { return new A() {}; }",
556             "  }",
557             "",
558             "  @Module",
559             "  static class Module03 {",
560             "    @Provides A provideA() { return new A() {}; }",
561             "  }",
562             "",
563             "  @Module",
564             "  static class Module04 {",
565             "    @Provides A provideA() { return new A() {}; }",
566             "  }",
567             "",
568             "  @Module",
569             "  static class Module05 {",
570             "    @Provides A provideA() { return new A() {}; }",
571             "  }",
572             "",
573             "  @Module",
574             "  static class Module06 {",
575             "    @Provides A provideA() { return new A() {}; }",
576             "  }",
577             "",
578             "  @Module",
579             "  static class Module07 {",
580             "    @Provides A provideA() { return new A() {}; }",
581             "  }",
582             "",
583             "  @Module",
584             "  static class Module08 {",
585             "    @Provides A provideA() { return new A() {}; }",
586             "  }",
587             "",
588             "  @Module",
589             "  static class Module09 {",
590             "    @Provides A provideA() { return new A() {}; }",
591             "  }",
592             "",
593             "  @Module",
594             "  static class Module10 {",
595             "    @Provides A provideA() { return new A() {}; }",
596             "  }",
597             "",
598             "  @Module",
599             "  static class Module11 {",
600             "    @Provides A provideA() { return new A() {}; }",
601             "  }",
602             "",
603             "  @Module",
604             "  static class Module12 {",
605             "    @Provides A provideA() { return new A() {}; }",
606             "  }",
607             "",
608             "  @Module(includes = {",
609             "    Module01.class,",
610             "    Module02.class,",
611             "    Module03.class,",
612             "    Module04.class,",
613             "    Module05.class,",
614             "    Module06.class,",
615             "    Module07.class,",
616             "    Module08.class,",
617             "    Module09.class,",
618             "    Module10.class,",
619             "    Module11.class,",
620             "    Module12.class",
621             "  })",
622             "  abstract static class Modules {}",
623             "",
624             "  @Component(modules = {",
625             "    Module01.class,",
626             "    Module02.class,",
627             "    Module03.class,",
628             "    Module04.class,",
629             "    Module05.class,",
630             "    Module06.class,",
631             "    Module07.class,",
632             "    Module08.class,",
633             "    Module09.class,",
634             "    Module10.class,",
635             "    Module11.class,",
636             "    Module12.class",
637             "  })",
638             "  interface TestComponent {",
639             "    A getA();",
640             "  }",
641             "}");
642 
643     CompilerTests.daggerCompiler(component)
644         .withProcessingOptions(
645             ImmutableMap.<String, String>builder()
646                 .putAll(fullBindingGraphValidationOption())
647                 .buildOrThrow())
648         .compile(
649             subject -> {
650               subject.hasErrorCount(fullBindingGraphValidation ? 2 : 1);
651               String errorMessage =
652                   message(
653                       "Outer.A is bound multiple times:",
654                       "    @Provides Outer.A Outer.Module01.provideA()",
655                       "    @Provides Outer.A Outer.Module02.provideA()",
656                       "    @Provides Outer.A Outer.Module03.provideA()",
657                       "    @Provides Outer.A Outer.Module04.provideA()",
658                       "    @Provides Outer.A Outer.Module05.provideA()",
659                       "    @Provides Outer.A Outer.Module06.provideA()",
660                       "    @Provides Outer.A Outer.Module07.provideA()",
661                       "    @Provides Outer.A Outer.Module08.provideA()",
662                       "    @Provides Outer.A Outer.Module09.provideA()",
663                       "    @Provides Outer.A Outer.Module10.provideA()",
664                       "    and 2 others");
665 
666               subject.hasErrorContaining(errorMessage)
667                   .onSource(component)
668                   .onLineContaining("interface TestComponent");
669 
670               if (fullBindingGraphValidation) {
671                 subject.hasErrorContaining(errorMessage)
672                     .onSource(component)
673                     .onLineContaining("class Modules");
674               }
675             });
676   }
677 
678   @Test
childBindingConflictsWithParent()679   public void childBindingConflictsWithParent() {
680     Source aComponent =
681         CompilerTests.javaSource(
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 abConflict() {",
698             "      return \"a\";",
699             "    }",
700             "  }",
701             "}");
702     Source bComponent =
703         CompilerTests.javaSource(
704             "test.B",
705             "package test;",
706             "",
707             "import dagger.Module;",
708             "import dagger.Provides;",
709             "import dagger.Subcomponent;",
710             "",
711             "@Subcomponent(modules = B.BModule.class)",
712             "interface B {",
713             "  Object conflict();",
714             "",
715             "  @Subcomponent.Builder",
716             "  interface Builder {",
717             "    B build();",
718             "  }",
719             "",
720             "  @Module",
721             "  static class BModule {",
722             "    @Provides static Object abConflict() {",
723             "      return \"b\";",
724             "    }",
725             "  }",
726             "}");
727 
728     CompilerTests.daggerCompiler(aComponent, bComponent)
729         .withProcessingOptions(
730             ImmutableMap.<String, String>builder()
731                 .putAll(fullBindingGraphValidationOption())
732                 .buildOrThrow())
733         .compile(
734             subject -> {
735               String errorMessage =
736                   message(
737                       "Object is bound multiple times:",
738                       "    @Provides Object test.A.AModule.abConflict()",
739                       "    @Provides Object test.B.BModule.abConflict()");
740               if (fullBindingGraphValidation) {
741                 subject.hasErrorCount(2);
742                 subject.hasErrorContaining("test.A.AModule has errors")
743                     .onSource(aComponent)
744                     .onLineContaining("@Component(");
745                 subject.hasErrorContaining(errorMessage)
746                     .onSource(aComponent)
747                     .onLineContaining("class AModule");
748               } else {
749                 subject.hasErrorCount(1);
750                 subject.hasErrorContaining(errorMessage)
751                     .onSource(aComponent)
752                     .onLineContaining("interface A {");
753               }
754             });
755   }
756 
757   @Test
grandchildBindingConflictsWithGrandparent()758   public void grandchildBindingConflictsWithGrandparent() {
759     Source aComponent =
760         CompilerTests.javaSource(
761             "test.A",
762             "package test;",
763             "",
764             "import dagger.Component;",
765             "import dagger.Module;",
766             "import dagger.Provides;",
767             "",
768             "@Component(modules = A.AModule.class)",
769             "interface A {",
770             "  Object conflict();",
771             "",
772             "  B.Builder b();",
773             "",
774             "  @Module(subcomponents = B.class)",
775             "  static class AModule {",
776             "    @Provides static Object acConflict() {",
777             "      return \"a\";",
778             "    }",
779             "  }",
780             "}");
781     Source bComponent =
782         CompilerTests.javaSource(
783             "test.B",
784             "package test;",
785             "",
786             "import dagger.Subcomponent;",
787             "",
788             "@Subcomponent",
789             "interface B {",
790             "  C.Builder c();",
791             "",
792             "  @Subcomponent.Builder",
793             "  interface Builder {",
794             "    B build();",
795             "  }",
796             "}");
797     Source cComponent =
798         CompilerTests.javaSource(
799             "test.C",
800             "package test;",
801             "",
802             "import dagger.Module;",
803             "import dagger.Provides;",
804             "import dagger.Subcomponent;",
805             "",
806             "@Subcomponent(modules = C.CModule.class)",
807             "interface C {",
808             "  Object conflict();",
809             "",
810             "  @Subcomponent.Builder",
811             "  interface Builder {",
812             "    C build();",
813             "  }",
814             "",
815             "  @Module",
816             "  static class CModule {",
817             "    @Provides static Object acConflict() {",
818             "      return \"c\";",
819             "    }",
820             "  }",
821             "}");
822 
823     CompilerTests.daggerCompiler(aComponent, bComponent, cComponent)
824         .withProcessingOptions(
825             ImmutableMap.<String, String>builder()
826                 .putAll(fullBindingGraphValidationOption())
827                 .buildOrThrow())
828         .compile(
829             subject -> {
830               String errorMessage =
831                   message(
832                       "Object is bound multiple times:",
833                       "    @Provides Object test.A.AModule.acConflict()",
834                       "    @Provides Object test.C.CModule.acConflict()");
835               if (fullBindingGraphValidation) {
836                 subject.hasErrorCount(2);
837                 subject.hasErrorContaining("test.A.AModule has errors")
838                     .onSource(aComponent)
839                     .onLineContaining("@Component(");
840                 subject.hasErrorContaining(errorMessage)
841                     .onSource(aComponent)
842                     .onLineContaining("class AModule");
843               } else {
844                 subject.hasErrorCount(1);
845                 subject.hasErrorContaining(errorMessage)
846                     .onSource(aComponent)
847                     .onLineContaining("interface A {");
848               }
849             });
850   }
851 
852   @Test
grandchildBindingConflictsWithChild()853   public void grandchildBindingConflictsWithChild() {
854     Source aComponent =
855         CompilerTests.javaSource(
856             "test.A",
857             "package test;",
858             "",
859             "import dagger.Component;",
860             "",
861             "@Component",
862             "interface A {",
863             "  B b();",
864             "}");
865     Source bComponent =
866         CompilerTests.javaSource(
867             "test.B",
868             "package test;",
869             "",
870             "import dagger.Module;",
871             "import dagger.Provides;",
872             "import dagger.Subcomponent;",
873             "",
874             "@Subcomponent(modules = B.BModule.class)",
875             "interface B {",
876             "  Object conflict();",
877             "",
878             "  C.Builder c();",
879             "",
880             "  @Module(subcomponents = C.class)",
881             "  static class BModule {",
882             "    @Provides static Object bcConflict() {",
883             "      return \"b\";",
884             "    }",
885             "  }",
886             "}");
887     Source cComponent =
888         CompilerTests.javaSource(
889             "test.C",
890             "package test;",
891             "",
892             "import dagger.Module;",
893             "import dagger.Provides;",
894             "import dagger.Subcomponent;",
895             "",
896             "@Subcomponent(modules = C.CModule.class)",
897             "interface C {",
898             "  Object conflict();",
899             "",
900             "  @Subcomponent.Builder",
901             "  interface Builder {",
902             "    C build();",
903             "  }",
904             "",
905             "  @Module",
906             "  static class CModule {",
907             "    @Provides static Object bcConflict() {",
908             "      return \"c\";",
909             "    }",
910             "  }",
911             "}");
912 
913     CompilerTests.daggerCompiler(aComponent, bComponent, cComponent)
914         .withProcessingOptions(
915             ImmutableMap.<String, String>builder()
916                 .putAll(fullBindingGraphValidationOption())
917                 .buildOrThrow())
918         .compile(
919             subject -> {
920               String errorMessage =
921                   message(
922                       "Object is bound multiple times:",
923                       "    @Provides Object test.B.BModule.bcConflict()",
924                       "    @Provides Object test.C.CModule.bcConflict()");
925               if (fullBindingGraphValidation) {
926                 subject.hasErrorCount(2);
927                 subject.hasErrorContaining("test.B.BModule has errors")
928                     .onSource(bComponent)
929                     .onLineContaining("@Subcomponent(modules = B.BModule.class)");
930                 subject.hasErrorContaining(errorMessage)
931                     .onSource(bComponent)
932                     .onLineContaining("class BModule");
933               } else {
934                 subject.hasErrorCount(1);
935                 subject.hasErrorContaining(errorMessage)
936                     .onSource(aComponent)
937                     .onLineContaining("interface A {");
938               }
939             });
940   }
941 
942   @Test
childProvidesConflictsWithParentInjects()943   public void childProvidesConflictsWithParentInjects() {
944     assumeFalse(fullBindingGraphValidation);
945 
946     Source foo =
947         CompilerTests.javaSource(
948             "test.Foo",
949             "package test;",
950             "",
951             "import java.util.Set;",
952             "import javax.inject.Inject;",
953             "",
954             "final class Foo {",
955             "  @Inject Foo(Set<String> strings) {}",
956             "}");
957     Source injected1 =
958         CompilerTests.javaSource(
959             "test.Injected1",
960             "package test;",
961             "",
962             "import dagger.Component;",
963             "import dagger.Module;",
964             "import dagger.Provides;",
965             "import dagger.multibindings.IntoSet;",
966             "import java.util.Set;",
967             "",
968             "@Component(modules = Injected1.Injected1Module.class)",
969             "interface Injected1 {",
970             "  Foo foo();",
971             "  Injected2 injected2();",
972             "",
973             "  @Module",
974             "  interface Injected1Module {",
975             "    @Provides @IntoSet static String string() {",
976             "      return \"injected1\";",
977             "    }",
978             "  }",
979             "}");
980     Source injected2 =
981         CompilerTests.javaSource(
982             "test.Injected2",
983             "package test;",
984             "",
985             "import dagger.Module;",
986             "import dagger.Provides;",
987             "import dagger.Subcomponent;",
988             "import dagger.multibindings.IntoSet;",
989             "import java.util.Set;",
990             "",
991             "@Subcomponent(modules = Injected2.Injected2Module.class)",
992             "interface Injected2 {",
993             "  Foo foo();",
994             "  Provided1 provided1();",
995             "",
996             "  @Module",
997             "  interface Injected2Module {",
998             "    @Provides @IntoSet static String string() {",
999             "      return \"injected2\";",
1000             "    }",
1001             "  }",
1002             "}");
1003     Source provided1 =
1004         CompilerTests.javaSource(
1005             "test.Provided1",
1006             "package test;",
1007             "",
1008             "import dagger.Module;",
1009             "import dagger.Provides;",
1010             "import dagger.Subcomponent;",
1011             "import dagger.multibindings.IntoSet;",
1012             "import java.util.Set;",
1013             "",
1014             "@Subcomponent(modules = Provided1.Provided1Module.class)",
1015             "interface Provided1 {",
1016             "  Foo foo();",
1017             "  Provided2 provided2();",
1018             "",
1019             "  @Module",
1020             "  static class Provided1Module {",
1021             "    @Provides static Foo provideFoo(Set<String> strings) {",
1022             "      return new Foo(strings);",
1023             "    }",
1024             "",
1025             "    @Provides @IntoSet static String string() {",
1026             "      return \"provided1\";",
1027             "    }",
1028             "  }",
1029             "}");
1030     Source provided2 =
1031         CompilerTests.javaSource(
1032             "test.Provided2",
1033             "package test;",
1034             "",
1035             "import dagger.Module;",
1036             "import dagger.Provides;",
1037             "import dagger.Subcomponent;",
1038             "import dagger.multibindings.IntoSet;",
1039             "",
1040             "@Subcomponent(modules = Provided2.Provided2Module.class)",
1041             "interface Provided2 {",
1042             "  Foo foo();",
1043             "",
1044             "  @Module",
1045             "  static class Provided2Module {",
1046             "    @Provides @IntoSet static String string() {",
1047             "      return \"provided2\";",
1048             "    }",
1049             "  }",
1050             "}");
1051 
1052     CompilerTests.daggerCompiler(foo, injected1, injected2, provided1, provided2)
1053         .compile(
1054             subject -> {
1055               subject.hasErrorCount(1);
1056               subject.hasErrorContaining(
1057                       message(
1058                           "Foo is bound multiple times:",
1059                           "    @Inject Foo(Set<String>) [Injected1]",
1060                           "    @Provides Foo Provided1.Provided1Module.provideFoo(Set<String>) "
1061                               + "[Injected1 → Injected2 → Provided1]"))
1062                   .onSource(injected1)
1063                   .onLineContaining("interface Injected1 {");
1064             });
1065   }
1066 
1067   @Test
grandchildBindingConflictsWithParentWithNullableViolationAsWarning()1068   public void grandchildBindingConflictsWithParentWithNullableViolationAsWarning() {
1069     Source parentConflictsWithChild =
1070         CompilerTests.javaSource(
1071             "test.ParentConflictsWithChild",
1072             "package test;",
1073             "",
1074             "import dagger.Component;",
1075             "import dagger.Module;",
1076             "import dagger.Provides;",
1077             "import javax.annotation.Nullable;",
1078             "",
1079             "@Component(modules = ParentConflictsWithChild.ParentModule.class)",
1080             "interface ParentConflictsWithChild {",
1081             "  Child.Builder child();",
1082             "",
1083             "  @Module(subcomponents = Child.class)",
1084             "  static class ParentModule {",
1085             "    @Provides @Nullable static Object nullableParentChildConflict() {",
1086             "      return \"parent\";",
1087             "    }",
1088             "  }",
1089             "}");
1090     Source child =
1091         CompilerTests.javaSource(
1092             "test.Child",
1093             "package test;",
1094             "",
1095             "import dagger.Module;",
1096             "import dagger.Provides;",
1097             "import dagger.Subcomponent;",
1098             "",
1099             "@Subcomponent(modules = Child.ChildModule.class)",
1100             "interface Child {",
1101             "  Object parentChildConflictThatViolatesNullability();",
1102             "",
1103             "  @Subcomponent.Builder",
1104             "  interface Builder {",
1105             "    Child build();",
1106             "  }",
1107             "",
1108             "  @Module",
1109             "  static class ChildModule {",
1110             "    @Provides static Object nonNullableParentChildConflict() {",
1111             "      return \"child\";",
1112             "    }",
1113             "  }",
1114             "}");
1115 
1116     CompilerTests.daggerCompiler(parentConflictsWithChild, child)
1117         .withProcessingOptions(
1118             ImmutableMap.<String, String>builder()
1119                 .put("dagger.nullableValidation", "WARNING")
1120                 .putAll(fullBindingGraphValidationOption())
1121                 .buildOrThrow())
1122         .compile(
1123             subject -> {
1124               String errorMessage =
1125                   message(
1126                       "Object is bound multiple times:",
1127                       "    @Provides Object Child.ChildModule.nonNullableParentChildConflict()",
1128                       "    @Provides @Nullable Object"
1129                           + " ParentConflictsWithChild.ParentModule.nullableParentChildConflict()");
1130               if (fullBindingGraphValidation) {
1131                 subject.hasErrorCount(2);
1132                 subject.hasErrorContaining(errorMessage)
1133                     .onSource(parentConflictsWithChild)
1134                     .onLineContaining("class ParentModule");
1135                 subject.hasErrorContaining(
1136                         "Object is not nullable, but is being provided by @Provides @Nullable "
1137                             + "Object")
1138                     .onSource(parentConflictsWithChild)
1139                     .onLineContaining("class ParentModule");
1140               } else {
1141                 subject.hasErrorCount(1);
1142                 subject.hasErrorContaining(errorMessage)
1143                     .onSource(parentConflictsWithChild)
1144                     .onLineContaining("interface ParentConflictsWithChild");
1145               }
1146             });
1147   }
1148 
fullBindingGraphValidationOption()1149   private ImmutableMap<String, String> fullBindingGraphValidationOption() {
1150     return ImmutableMap.of(
1151         "dagger.fullBindingGraphValidation",
1152         fullBindingGraphValidation ? "ERROR" : "NONE");
1153   }
1154 
1155   @Test
reportedInParentAndChild()1156   public void reportedInParentAndChild() {
1157     Source parent =
1158         CompilerTests.javaSource(
1159             "test.Parent",
1160             "package test;",
1161             "",
1162             "import dagger.Component;",
1163             "",
1164             "@Component(modules = ParentModule.class)",
1165             "interface Parent {",
1166             "  Child.Builder childBuilder();",
1167             "  String duplicated();",
1168             "}");
1169     Source parentModule =
1170         CompilerTests.javaSource(
1171             "test.ParentModule",
1172             "package test;",
1173             "",
1174             "import dagger.BindsOptionalOf;",
1175             "import dagger.Module;",
1176             "import dagger.Provides;",
1177             "import java.util.Optional;",
1178             "",
1179             "@Module",
1180             "interface ParentModule {",
1181             "  @Provides static String one(Optional<Object> optional) { return \"one\"; }",
1182             "  @Provides static String two() { return \"two\"; }",
1183             "  @BindsOptionalOf Object optional();",
1184             "}");
1185     Source child =
1186         CompilerTests.javaSource(
1187             "test.Child",
1188             "package test;",
1189             "",
1190             "import dagger.Subcomponent;",
1191             "",
1192             "@Subcomponent(modules = ChildModule.class)",
1193             "interface Child {",
1194             "  String duplicated();",
1195             "",
1196             "  @Subcomponent.Builder",
1197             "  interface Builder {",
1198             "    Child build();",
1199             "  }",
1200             "}");
1201     Source childModule =
1202         CompilerTests.javaSource(
1203             "test.ChildModule",
1204             "package test;",
1205             "",
1206             "import dagger.Module;",
1207             "import dagger.Provides;",
1208             "import java.util.Optional;",
1209             "",
1210             "@Module",
1211             "interface ChildModule {",
1212             "  @Provides static Object object() { return \"object\"; }",
1213             "}");
1214     CompilerTests.daggerCompiler(parent, parentModule, child, childModule)
1215         .compile(
1216             subject -> {
1217               subject.hasErrorCount(1);
1218               subject.hasErrorContaining("String is bound multiple times")
1219                   .onSource(parent)
1220                   .onLineContaining("interface Parent");
1221             });
1222   }
1223 
1224   // Tests the format of the error for a somewhat complex binding method.
1225   @Test
formatTest()1226   public void formatTest() {
1227     Source modules =
1228         CompilerTests.javaSource(
1229             "test.Modules",
1230             "package test;",
1231             "",
1232             "import com.google.common.collect.ImmutableList;",
1233             "import dagger.Module;",
1234             "import dagger.Provides;",
1235             "import javax.inject.Singleton;",
1236             "",
1237             "interface Modules {",
1238             "  @interface Foo {",
1239             "    Class<?> bar();",
1240             "  }",
1241             "",
1242             "  @Module",
1243             "  interface Module1 {",
1244             "    @Provides",
1245             "    @Singleton",
1246             "    @Foo(bar = String.class)",
1247             "    static String foo(",
1248             "        @SuppressWarnings(\"unused\") int a,",
1249             "        @SuppressWarnings(\"unused\") ImmutableList<Boolean> blah) {",
1250             "      return \"\";",
1251             "    }",
1252             "  }",
1253             "",
1254             "  @Module",
1255             "  interface Module2 {",
1256             "    @Provides",
1257             "    @Singleton",
1258             "    @Foo(bar = String.class)",
1259             "    static String foo(",
1260             "        @SuppressWarnings(\"unused\") int a,",
1261             "        @SuppressWarnings(\"unused\") ImmutableList<Boolean> blah) {",
1262             "      return \"\";",
1263             "    }",
1264             "  }",
1265             "}");
1266     Source component =
1267         CompilerTests.javaSource(
1268             "test.TestComponent",
1269             "package test;",
1270             "",
1271             "import dagger.BindsInstance;",
1272             "import dagger.Component;",
1273             "import javax.inject.Singleton;",
1274             "",
1275             "@Singleton",
1276             "@Component(modules = {Modules.Module1.class, Modules.Module2.class})",
1277             "interface TestComponent {",
1278             "  @Modules.Foo(bar = String.class) String foo();",
1279             "}");
1280     CompilerTests.daggerCompiler(modules, component)
1281         .compile(
1282             subject -> {
1283               subject.hasErrorCount(1);
1284               subject.hasErrorContaining(
1285                   String.format(
1286                       String.join(
1287                           "\n",
1288                           "String is bound multiple times:",
1289                           "    @Provides @Singleton @Modules.Foo(%1$s) String "
1290                               + "Modules.Module1.foo(int, ImmutableList<Boolean>)",
1291                           "    @Provides @Singleton @Modules.Foo(%1$s) String "
1292                               + "Modules.Module2.foo(int, ImmutableList<Boolean>)"),
1293                       // TODO(b/241293838): KSP and java should match after this is fixed.
1294                       CompilerTests.backend(subject) == XProcessingEnv.Backend.KSP
1295                           ? "bar=String"
1296                           : "bar = String.class"));
1297             });
1298   }
1299 }
1300