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