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