• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 androidx.room.compiler.processing.util.CompilationResultSubject;
20 import com.google.common.base.Joiner;
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.ImmutableMap;
23 import dagger.testing.compile.CompilerTests;
24 import java.util.function.Consumer;
25 import org.junit.Test;
26 import org.junit.runner.RunWith;
27 import org.junit.runners.Parameterized;
28 import org.junit.runners.Parameterized.Parameters;
29 
30 @RunWith(Parameterized.class)
31 public class IgnoreProvisionKeyWildcardsTest {
32   enum SourceKind { JAVA, KOTLIN }
33 
34   @Parameters(name = "sourceKind={0}, isIgnoreProvisionKeyWildcardsEnabled={1}")
parameters()35   public static ImmutableList<Object[]> parameters() {
36     return ImmutableList.of(
37         new Object[] {SourceKind.JAVA, false},
38         new Object[] {SourceKind.KOTLIN, false},
39         new Object[] {SourceKind.JAVA, true},
40         new Object[] {SourceKind.KOTLIN, true}
41     );
42   }
43 
44   private static final Joiner NEW_LINES = Joiner.on("\n");
45   private static final Joiner NEW_LINES_FOR_ERROR_MSG = Joiner.on("\n      ");
46 
47   private final boolean isIgnoreProvisionKeyWildcardsEnabled;
48   private final SourceKind sourceKind;
49   private final ImmutableMap<String, String> processingOptions;
50 
IgnoreProvisionKeyWildcardsTest( SourceKind sourceKind, boolean isIgnoreProvisionKeyWildcardsEnabled)51   public IgnoreProvisionKeyWildcardsTest(
52       SourceKind sourceKind,
53       boolean isIgnoreProvisionKeyWildcardsEnabled) {
54     this.sourceKind = sourceKind;
55     this.isIgnoreProvisionKeyWildcardsEnabled = isIgnoreProvisionKeyWildcardsEnabled;
56     processingOptions =
57         isIgnoreProvisionKeyWildcardsEnabled
58             ? ImmutableMap.of("dagger.ignoreProvisionKeyWildcards", "enabled")
59             : ImmutableMap.of("dagger.ignoreProvisionKeyWildcards", "disabled");
60   }
61 
62   @Test
testProvidesUniqueBindingsWithDifferentTypeVariances()63   public void testProvidesUniqueBindingsWithDifferentTypeVariances() {
64     compile(
65         /* javaComponentClass = */
66         NEW_LINES.join(
67             "@Component(modules = MyModule.class)",
68             "interface MyComponent {",
69             "  Foo<? extends Bar> fooExtends();",
70             "  Foo<Bar> foo();",
71             "}",
72             "@Module",
73             "interface MyModule {",
74             "  @Provides static Foo<? extends Bar> fooExtends() { return null; }",
75             "  @Provides static Foo<Bar> foo() { return null; }",
76             "}"),
77         /* kotlinComponentClass = */
78         NEW_LINES.join(
79             "@Component(modules = [MyModule::class])",
80             "interface MyComponent {",
81             "  fun fooExtends(): Foo<out Bar>",
82             "  fun foo(): Foo<Bar>",
83             "}",
84             "@Module",
85             "object MyModule {",
86             "  @Provides fun fooExtends(): Foo<out Bar> = TODO()",
87             "  @Provides fun foo(): Foo<Bar> = TODO()",
88             "}"),
89         subject -> {
90           if (isIgnoreProvisionKeyWildcardsEnabled) {
91             subject.hasErrorCount(1);
92             subject.hasErrorContaining(
93                 NEW_LINES_FOR_ERROR_MSG.join(
94                     "Foo<? extends Bar> is bound multiple times:",
95                     "        @Provides Foo<Bar> MyModule.foo()",
96                     "        @Provides Foo<? extends Bar> MyModule.fooExtends()",
97                     "    in component: [MyComponent]"));
98           } else {
99             subject.hasErrorCount(0);
100           }
101         });
102   }
103 
104   @Test
testProvidesUniqueBindingsWithMatchingWildcardArguments()105   public void testProvidesUniqueBindingsWithMatchingWildcardArguments() {
106     compile(
107         /* javaComponentClass = */
108         NEW_LINES.join(
109             "@Component(modules = MyModule.class)",
110             "interface MyComponent {",
111             "  Map<Foo<? extends Bar>, Foo<? extends Bar>> mapFooExtendsBarFooExtendsBar();",
112             "  Map<Foo<? extends Bar>, Foo<Bar>> mapFooExtendsBarFooBar();",
113             "  Map<Foo<Bar>, Foo<? extends Bar>> mapFooBarFooExtendsBar();",
114             "  Map<Foo<Bar>, Foo<Bar>> mapFooBarFooBar();",
115             "}",
116             "@Module",
117             "class MyModule {",
118             "  @Provides",
119             "  Map<Foo<? extends Bar>, Foo<? extends Bar>> mapFooExtendsBarFooExtendsBar() {",
120             "    return null; ",
121             "  }",
122             "  @Provides",
123             "  Map<Foo<? extends Bar>, Foo<Bar>> mapFooExtendsBarFooBar() {",
124             "    return null;",
125             "  }",
126             "  @Provides",
127             "  Map<Foo<Bar>, Foo<? extends Bar>> mapFooBarFooExtendsBar() {",
128             "    return null;",
129             "  }",
130             "  @Provides",
131             "  Map<Foo<Bar>, Foo<Bar>> mapFooBarFooBar() {",
132             "    return null;",
133             "  }",
134             "}"),
135         /* kotlinComponentClass = */
136         NEW_LINES.join(
137             "@Component(modules = [MyModule::class])",
138             "interface MyComponent {",
139             "  fun mapFooExtendsBarFooExtendsBar(): Map<Foo<out Bar>, Foo<out Bar>>",
140             "  fun mapFooExtendsBarFooBar(): Map<Foo<out Bar>, Foo<Bar>>",
141             "  fun mapFooBarFooExtendsBar(): Map<Foo<Bar>, Foo<out Bar>>",
142             "  fun mapFooBarFooBar(): Map<Foo<Bar>, Foo<Bar>>",
143             "}",
144             "@Module",
145             "class MyModule {",
146             "  @Provides",
147             "  fun mapFooExtendsBarFooExtendsBar(): Map<Foo<out Bar>, Foo<out Bar>> = TODO()",
148             "  @Provides",
149             "  fun mapFooExtendsBarFooBar(): Map<Foo<out Bar>, Foo<Bar>> = TODO()",
150             "  @Provides",
151             "  fun mapFooBarFooExtendsBar(): Map<Foo<Bar>, Foo<out Bar>> = TODO()",
152             "  @Provides",
153             "  fun mapFooBarFooBar(): Map<Foo<Bar>, Foo<Bar>> = TODO()",
154             "}"),
155         subject -> {
156           if (isIgnoreProvisionKeyWildcardsEnabled) {
157             subject.hasErrorCount(1);
158             subject.hasErrorContaining(
159                 NEW_LINES_FOR_ERROR_MSG.join(
160                     "Map<Foo<? extends Bar>,Foo<? extends Bar>> is bound multiple times:",
161                     "        @Provides Map<Foo<Bar>,Foo<Bar>> MyModule.mapFooBarFooBar()",
162                     "        @Provides Map<Foo<Bar>,Foo<? extends Bar>> "
163                         + "MyModule.mapFooBarFooExtendsBar()",
164                     "        @Provides Map<Foo<? extends Bar>,Foo<Bar>> "
165                         + "MyModule.mapFooExtendsBarFooBar()",
166                     "        @Provides Map<Foo<? extends Bar>,Foo<? extends Bar>> "
167                         + "MyModule.mapFooExtendsBarFooExtendsBar()",
168                     "    in component: [MyComponent]"));
169           } else {
170             subject.hasErrorCount(0);
171           }
172         });
173   }
174 
175   @Test
testProvidesMultibindsSetDeclarationsWithDifferentTypeVariances()176   public void testProvidesMultibindsSetDeclarationsWithDifferentTypeVariances() {
177     compile(
178         /* javaComponentClass = */
179         NEW_LINES.join(
180             "@Component(modules = MyModule.class)",
181             "interface MyComponent {",
182             "  Set<Foo<? extends Bar>> setExtends();",
183             "  Set<Foo<Bar>> set();",
184             "}",
185             "@Module",
186             "interface MyModule {",
187             "  @Multibinds Set<Foo<? extends Bar>> setExtends();",
188             "  @Multibinds Set<Foo<Bar>> set();",
189             "}"),
190         /* kotlinComponentClass = */
191         NEW_LINES.join(
192             "@Component(modules = [MyModule::class])",
193             "interface MyComponent {",
194             "  fun setExtends(): Set<Foo<out Bar>>",
195             "  fun set(): Set<Foo<Bar>>",
196             "}",
197             "@Module",
198             "interface MyModule {",
199             "  @Multibinds fun setExtends(): Set<Foo<out Bar>>",
200             "  @Multibinds fun set(): Set<Foo<Bar>>",
201             "}"),
202         subject -> {
203           if (isIgnoreProvisionKeyWildcardsEnabled) {
204             subject.hasErrorCount(1);
205             subject.hasErrorContaining(
206                 NEW_LINES_FOR_ERROR_MSG.join(
207                     "Set<Foo<? extends Bar>> has incompatible bindings or declarations:",
208                     "    Set bindings and declarations:",
209                     "        @Multibinds Set<Foo<Bar>> MyModule.set()",
210                     "        @Multibinds Set<Foo<? extends Bar>> MyModule.setExtends()",
211                     "    in component: [MyComponent]"));
212           } else {
213             subject.hasErrorCount(0);
214           }
215         });
216   }
217 
218   @Test
testProvidesMultibindsSetContributionsWithDifferentTypeVariances()219   public void testProvidesMultibindsSetContributionsWithDifferentTypeVariances() {
220     compile(
221         /* javaComponentClass= */ NEW_LINES.join(
222             "@Component(modules = MyModule.class)",
223             "interface MyComponent {",
224             "  Set<Foo<? extends Bar>> setExtends();",
225             "  Set<Foo<Bar>> set();",
226             "}",
227             "@Module",
228             "interface MyModule {",
229             "  @Provides @IntoSet static Foo<? extends Bar> setExtends() { return null; }",
230             "  @Provides @IntoSet static Foo<Bar> set() { return null; }",
231             "}"),
232         /* kotlinComponentClass= */ NEW_LINES.join(
233             "@Component(modules = [MyModule::class])",
234             "interface MyComponent {",
235             "  fun setExtends(): Set<Foo<out Bar>>",
236             "  fun set(): Set<Foo<Bar>>",
237             "}",
238             "@Module",
239             "object MyModule {",
240             "  @Provides @IntoSet fun setExtends(): Foo<out Bar> = TODO()",
241             "  @Provides @IntoSet fun set(): Foo<Bar> = TODO()",
242             "}"),
243         subject -> {
244           if (isIgnoreProvisionKeyWildcardsEnabled) {
245             subject.hasErrorCount(1);
246             subject.hasErrorContaining(
247                 NEW_LINES_FOR_ERROR_MSG.join(
248                     "Set<Foo<? extends Bar>> has incompatible bindings or declarations:",
249                     "    Set bindings and declarations:",
250                     "        @Provides @IntoSet Foo<Bar> MyModule.set()",
251                     "        @Provides @IntoSet Foo<? extends Bar> MyModule.setExtends()",
252                     "    in component: [MyComponent]"));
253           } else {
254             subject.hasErrorCount(0);
255           }
256         });
257   }
258 
259   @Test
testProvidesMultibindsSetContributionAndMultibindsWithDifferentVariances()260   public void testProvidesMultibindsSetContributionAndMultibindsWithDifferentVariances() {
261     compile(
262         /* javaComponentClass = */
263         NEW_LINES.join(
264             "@Component(modules = MyModule.class)",
265             "interface MyComponent {",
266             "  Set<Foo<? extends Bar>> setExtends();",
267             "  Set<Foo<Bar>> set();",
268             "}",
269             "@Module",
270             "interface MyModule {",
271             "  @Provides @IntoSet static Foo<? extends Bar> setExtends() { return null; }",
272             "  @Multibinds Set<Foo<Bar>> mulitbindSet();",
273             "}"),
274         /* kotlinComponentClass = */
275         NEW_LINES.join(
276             "@Component(modules = [MyModule::class])",
277             "interface MyComponent {",
278             "  fun setExtends(): Set<Foo<out Bar>>",
279             "  fun set(): Set<Foo<Bar>>",
280             "}",
281             "@Module",
282             "interface MyModule {",
283             "  @Multibinds abstract fun mulitbindSet(): Set<Foo<Bar>>",
284             "",
285             "  companion object {",
286             "    @Provides @IntoSet fun setExtends(): Foo<out Bar> = TODO()",
287             "  }",
288             "}"),
289         subject -> {
290           if (isIgnoreProvisionKeyWildcardsEnabled) {
291             subject.hasErrorCount(1);
292             subject.hasErrorContaining(
293                 String.format(
294                     NEW_LINES_FOR_ERROR_MSG.join(
295                         "Set<Foo<? extends Bar>> has incompatible bindings or declarations:",
296                         "    Set bindings and declarations:",
297                         "        @Multibinds Set<Foo<Bar>> MyModule.mulitbindSet()",
298                         "        @Provides @IntoSet Foo<? extends Bar> %s.setExtends()",
299                         "    in component: [MyComponent]"),
300                     sourceKind == SourceKind.KOTLIN ? "MyModule.Companion" : "MyModule"));
301           } else {
302             subject.hasErrorCount(0);
303           }
304         });
305   }
306 
307   @Test
testProvidesIntoSetAndElementsIntoSetContributionsWithDifferentVariances()308   public void testProvidesIntoSetAndElementsIntoSetContributionsWithDifferentVariances() {
309     compile(
310         /* javaComponentClass= */ NEW_LINES.join(
311             "@Component(modules = MyModule.class)",
312             "interface MyComponent {",
313             "  Set<Foo<? extends Bar>> setExtends();",
314             "  Set<Foo<Bar>> set();",
315             "}",
316             "@Module",
317             "interface MyModule {",
318             "  @Provides @IntoSet static Foo<? extends Bar> setExtends() { return null; }",
319             "",
320             "  @Provides",
321             "  @ElementsIntoSet",
322             "  static Set<Foo<Bar>> set() { return null; }",
323             "}"),
324         /* kotlinComponentClass= */ NEW_LINES.join(
325             "@Component(modules = [MyModule::class])",
326             "interface MyComponent {",
327             "  fun setExtends(): Set<Foo<out Bar>>",
328             "  fun set(): Set<Foo<Bar>>",
329             "}",
330             "@Module",
331             "object MyModule {",
332             "  @Provides @IntoSet fun setExtends(): Foo<out Bar> = TODO()",
333             "  @Provides @ElementsIntoSet fun set(): Set<Foo<Bar>> = TODO()",
334             "}"),
335         subject -> {
336           if (isIgnoreProvisionKeyWildcardsEnabled) {
337             subject.hasErrorCount(1);
338             subject.hasErrorContaining(
339                 NEW_LINES_FOR_ERROR_MSG.join(
340                     "Set<Foo<? extends Bar>> has incompatible bindings or declarations:",
341                     "    Set bindings and declarations:",
342                     "        @Provides @ElementsIntoSet Set<Foo<Bar>> MyModule.set()",
343                     "        @Provides @IntoSet Foo<? extends Bar> MyModule.setExtends()",
344                     "    in component: [MyComponent]"));
345           } else {
346             subject.hasErrorCount(0);
347           }
348         });
349   }
350 
351   @Test
testProvidesMultibindsSetContributionsWithSameTypeVariances()352   public void testProvidesMultibindsSetContributionsWithSameTypeVariances() {
353     compile(
354         /* javaComponentClass = */
355         NEW_LINES.join(
356             "@Component(modules = MyModule.class)",
357             "interface MyComponent {",
358             "  Set<Foo<Bar>> set();",
359             "}",
360             "@Module",
361             "interface MyModule {",
362             "  @Provides @IntoSet static Foo<Bar> set1() { return null; }",
363             "  @Provides @IntoSet static Foo<Bar> set2() { return null; }",
364             "  @Provides @ElementsIntoSet static Set<Foo<Bar>> set3() { return null; }",
365             "}"),
366         /* kotlinComponentClass = */
367         NEW_LINES.join(
368             "@Component(modules = [MyModule::class])",
369             "interface MyComponent {",
370             "  fun set(): Set<Foo<Bar>>",
371             "}",
372             "@Module",
373             "object MyModule {",
374             "  @Provides @IntoSet fun set1(): Foo<Bar> = TODO()",
375             "  @Provides @IntoSet fun set2(): Foo<Bar> = TODO()",
376             "  @Provides @ElementsIntoSet fun set3(): Set<Foo<Bar>> = TODO()",
377             "}"),
378         subject -> subject.hasErrorCount(0));
379   }
380 
381   @Test
testProvidesMultibindsMapDeclarationValuesWithDifferentTypeVariances()382   public void testProvidesMultibindsMapDeclarationValuesWithDifferentTypeVariances() {
383     compile(
384         /* javaComponentClass = */
385         NEW_LINES.join(
386             "@Component(modules = MyModule.class)",
387             "interface MyComponent {",
388             "  Map<String, Foo<? extends Bar>> mapExtends();",
389             "  Map<String, Foo<Bar>> map();",
390             "}",
391             "@Module",
392             "interface MyModule {",
393             "  @Multibinds Map<String, Foo<? extends Bar>> mapExtends();",
394             "  @Multibinds Map<String, Foo<Bar>> map();",
395             "}"),
396         /* kotlinComponentClass = */
397         NEW_LINES.join(
398             "@Component(modules = [MyModule::class])",
399             "interface MyComponent {",
400             "  fun mapExtends(): Map<String, Foo<out Bar>>",
401             "  fun map(): Map<String, Foo<Bar>>",
402             "}",
403             "@Module",
404             "interface MyModule {",
405             "  @Multibinds fun mapExtends():Map<String, Foo<out Bar>>",
406             "  @Multibinds fun map(): Map<String, Foo<Bar>>",
407             "}"),
408         subject -> {
409           if (isIgnoreProvisionKeyWildcardsEnabled) {
410             subject.hasErrorCount(1);
411             subject.hasErrorContaining(
412                 NEW_LINES_FOR_ERROR_MSG.join(
413                     "Map<String,Foo<? extends Bar>> has incompatible bindings or declarations:",
414                     "    Map bindings and declarations:",
415                     "        @Multibinds Map<String,Foo<Bar>> MyModule.map()",
416                     "        @Multibinds Map<String,Foo<? extends Bar>> MyModule.mapExtends()",
417                     "    in component: [MyComponent]"));
418           } else {
419             subject.hasErrorCount(0);
420           }
421         });
422   }
423 
424   @Test
testProvidesMultibindsMapDeclarationKeysWithDifferentTypeVariances()425   public void testProvidesMultibindsMapDeclarationKeysWithDifferentTypeVariances() {
426     compile(
427         /* javaComponentClass = */
428         NEW_LINES.join(
429             "@Component(modules = MyModule.class)",
430             "interface MyComponent {",
431             "  Map<Foo<? extends Bar>, String> mapExtends();",
432             "  Map<Foo<Bar>, String> map();",
433             "}",
434             "@Module",
435             "interface MyModule {",
436             "  @Multibinds Map<Foo<? extends Bar>, String> mapExtends();",
437             "  @Multibinds Map<Foo<Bar>, String> map();",
438             "}"),
439         /* kotlinComponentClass = */
440         NEW_LINES.join(
441             "@Component(modules = [MyModule::class])",
442             "interface MyComponent {",
443             "  fun mapExtends(): Map<Foo<out Bar>, String>",
444             "  fun map(): Map<Foo<Bar>, String>",
445             "}",
446             "@Module",
447             "interface MyModule {",
448             "  @Multibinds fun mapExtends():Map<Foo<out Bar>, String>",
449             "  @Multibinds fun map(): Map<Foo<Bar>, String>",
450             "}"),
451         subject -> {
452           if (isIgnoreProvisionKeyWildcardsEnabled) {
453             subject.hasErrorCount(1);
454             subject.hasErrorContaining(
455                 NEW_LINES_FOR_ERROR_MSG.join(
456                     "Map<Foo<? extends Bar>,String> has incompatible bindings or declarations:",
457                     "    Map bindings and declarations:",
458                     "        @Multibinds Map<Foo<Bar>,String> MyModule.map()",
459                     "        @Multibinds Map<Foo<? extends Bar>,String> MyModule.mapExtends()",
460                     "    in component: [MyComponent]"));
461           } else {
462             subject.hasErrorCount(0);
463           }
464         });
465   }
466 
467   @Test
testProvidesMultibindsMapContributionsWithDifferentTypeVariances()468   public void testProvidesMultibindsMapContributionsWithDifferentTypeVariances() {
469     compile(
470         /* javaComponentClass = */
471         NEW_LINES.join(
472             "@Component(modules = MyModule.class)",
473             "interface MyComponent {",
474             "  Map<String, Foo<? extends Bar>> mapExtends();",
475             "  Map<String, Foo<Bar>> map();",
476             "}",
477             "@Module",
478             "interface MyModule {",
479             "  @Provides",
480             "  @IntoMap",
481             "  @StringKey(\"fooExtends\")",
482             "  static Foo<? extends Bar> fooExtends() { return null; }",
483             "",
484             "  @Provides",
485             "  @IntoMap",
486             "  @StringKey(\"foo\")",
487             "  static Foo<Bar> foo() { return null; }",
488             "}"),
489         /* kotlinComponentClass = */
490         NEW_LINES.join(
491             "@Component(modules = [MyModule::class])",
492             "interface MyComponent {",
493             "  fun mapExtends(): Map<String, Foo<out Bar>>",
494             "  fun map(): Map<String, Foo<Bar>>",
495             "}",
496             "@Module",
497             "object MyModule {",
498             "  @Provides",
499             "  @IntoMap",
500             "  @StringKey(\"fooExtends\")",
501             "  fun fooExtends(): Foo<out Bar> = TODO()",
502             "",
503             "  @Provides",
504             "  @IntoMap",
505             "  @StringKey(\"foo\")",
506             "  fun foo(): Foo<Bar> = TODO()",
507             "}"),
508         subject -> {
509           if (isIgnoreProvisionKeyWildcardsEnabled) {
510             subject.hasErrorContaining(
511                 String.format(
512                     NEW_LINES_FOR_ERROR_MSG.join(
513                         "Map<String,Foo<? extends Bar>> has incompatible bindings or declarations:",
514                         "    Map bindings and declarations:",
515                         "        %s Foo<Bar> MyModule.foo()",
516                         "        %s Foo<? extends Bar> MyModule.fooExtends()",
517                         "    in component: [MyComponent]"),
518                     "@Provides @IntoMap @StringKey(\"foo\")",
519                     "@Provides @IntoMap @StringKey(\"fooExtends\")"));
520           } else {
521             subject.hasErrorCount(0);
522           }
523         });
524   }
525 
526   @Test
testProvidesOptionalDeclarationWithDifferentTypeVariances()527   public void testProvidesOptionalDeclarationWithDifferentTypeVariances() {
528     compile(
529         /* javaComponentClass = */
530         NEW_LINES.join(
531             "@Component(modules = MyModule.class)",
532             "interface MyComponent {",
533             "  Optional<Foo<? extends Bar>> fooExtends();",
534             "  Optional<Foo<Bar>> foo();",
535             "}",
536             "@Module",
537             "interface MyModule {",
538             "  @BindsOptionalOf Foo<? extends Bar> fooExtends();",
539             "  @BindsOptionalOf Foo<Bar> foo();",
540             "}"),
541         /* kotlinComponentClass = */
542         NEW_LINES.join(
543             "@Component(modules = [MyModule::class])",
544             "interface MyComponent {",
545             "  fun fooExtends(): Optional<Foo<out Bar>>",
546             "  fun foo(): Optional<Foo<Bar>>",
547             "}",
548             "@Module",
549             "interface MyModule {",
550             "  @BindsOptionalOf fun fooExtends(): Foo<out Bar>",
551             "  @BindsOptionalOf fun foo(): Foo<Bar>",
552             "}"),
553         subject -> {
554           if (isIgnoreProvisionKeyWildcardsEnabled) {
555             subject.hasErrorCount(1);
556             subject.hasErrorContaining(
557                 NEW_LINES_FOR_ERROR_MSG.join(
558                     "Optional<Foo<? extends Bar>> is bound multiple times:",
559                     "    @BindsOptionalOf Foo<Bar> MyModule.foo()",
560                     "    @BindsOptionalOf Foo<? extends Bar> MyModule.fooExtends()",
561                     "in component: [MyComponent]"));
562           } else {
563             subject.hasErrorCount(0);
564           }
565         });
566   }
567 
compile( String javaComponentClass, String kotlinComponentClass, Consumer<CompilationResultSubject> onCompilationResult)568   private void compile(
569       String javaComponentClass,
570       String kotlinComponentClass,
571       Consumer<CompilationResultSubject> onCompilationResult) {
572     compileInternal(
573         javaComponentClass,
574         kotlinComponentClass,
575         subject -> {
576           if (!isIgnoreProvisionKeyWildcardsEnabled) {
577             if (CompilerTests.backend(subject) ==
578                 androidx.room.compiler.processing.XProcessingEnv.Backend.KSP) {
579               subject.hasErrorCount(1);
580               subject.hasErrorContaining(
581                   "When using KSP, you must also enable the 'dagger.ignoreProvisionKeyWildcards'");
582               return;
583             }
584           }
585           onCompilationResult.accept(subject);
586         });
587   }
588 
compileInternal( String javaComponentClass, String kotlinComponentClass, Consumer<CompilationResultSubject> onCompilationResult)589   private void compileInternal(
590       String javaComponentClass,
591       String kotlinComponentClass,
592       Consumer<CompilationResultSubject> onCompilationResult) {
593     if (sourceKind == SourceKind.JAVA) {
594       // Compile with Java sources
595       CompilerTests.daggerCompiler(
596               CompilerTests.javaSource(
597                   "test.MyComponent",
598                   "package test;",
599                   "",
600                   "import dagger.BindsOptionalOf;",
601                   "import dagger.Component;",
602                   "import dagger.Module;",
603                   "import dagger.Provides;",
604                   "import dagger.multibindings.ElementsIntoSet;",
605                   "import dagger.multibindings.IntoSet;",
606                   "import dagger.multibindings.IntoMap;",
607                   "import dagger.multibindings.Multibinds;",
608                   "import dagger.multibindings.StringKey;",
609                   "import java.util.Map;",
610                   "import java.util.Optional;",
611                   "import java.util.Set;",
612                   "import javax.inject.Inject;",
613                   "import javax.inject.Provider;",
614                   "",
615                   javaComponentClass,
616                   "",
617                   "interface Foo<T> {}",
618                   "",
619                   "class Bar {}"))
620           .withProcessingOptions(processingOptions)
621           .compile(onCompilationResult);
622     }
623 
624     if (sourceKind == SourceKind.KOTLIN) {
625       // Compile with Kotlin sources
626       CompilerTests.daggerCompiler(
627               CompilerTests.kotlinSource(
628                   "test.MyComponent.kt",
629                   // TODO(bcorso): See if there's a better way to fix the following error.
630                   //
631                   //   Error: Cannot inline bytecode built with JVM target 11 into bytecode that is
632                   //          being built with JVM target 1.8
633                   "@file:Suppress(\"INLINE_FROM_HIGHER_PLATFORM\")",
634                   "package test",
635                   "",
636                   "import dagger.BindsOptionalOf",
637                   "import dagger.Component",
638                   "import dagger.Module",
639                   "import dagger.Provides",
640                   "import dagger.multibindings.ElementsIntoSet",
641                   "import dagger.multibindings.IntoSet",
642                   "import dagger.multibindings.IntoMap",
643                   "import dagger.multibindings.Multibinds",
644                   "import dagger.multibindings.StringKey",
645                   "import java.util.Optional;",
646                   "import javax.inject.Inject",
647                   "import javax.inject.Provider",
648                   "",
649                   kotlinComponentClass,
650                   "",
651                   "interface Foo<T>",
652                   "",
653                   "class Bar"))
654           .withProcessingOptions(processingOptions)
655           .compile(onCompilationResult);
656     }
657   }
658 }
659