xref: /aosp_15_r20/external/dagger2/javatests/dagger/internal/codegen/MembersInjectionValidationTest.java (revision f585d8a307d0621d6060bd7e80091fdcbf94fe27)
1 /*
2  * Copyright (C) 2018 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen;
18 
19 import androidx.room.compiler.processing.util.Source;
20 import dagger.testing.compile.CompilerTests;
21 import org.junit.Test;
22 import org.junit.runner.RunWith;
23 import org.junit.runners.JUnit4;
24 
25 /**
26  * Tests that errors are reported for invalid members injection methods and {@link
27  * dagger.MembersInjector} dependency requests.
28  */
29 @RunWith(JUnit4.class)
30 public class MembersInjectionValidationTest {
31   @Test
membersInjectDependsOnUnboundedType()32   public void membersInjectDependsOnUnboundedType() {
33     Source injectsUnboundedType =
34         CompilerTests.javaSource(
35             "test.InjectsUnboundedType",
36             "package test;",
37             "",
38             "import dagger.MembersInjector;",
39             "import java.util.ArrayList;",
40             "import javax.inject.Inject;",
41             "",
42             "class InjectsUnboundedType {",
43             "  @Inject MembersInjector<ArrayList<?>> listInjector;",
44             "}");
45 
46     CompilerTests.daggerCompiler(injectsUnboundedType)
47         .compile(
48             subject -> {
49               subject.hasErrorCount(1);
50               subject.hasErrorContaining(
51                       "Cannot inject members into types with unbounded type arguments: "
52                           + "java.util.ArrayList<?>")
53                   .onSource(injectsUnboundedType)
54                   .onLineContaining("@Inject MembersInjector<ArrayList<?>> listInjector;");
55             });
56   }
57 
58   @Test
membersInjectPrimitive()59   public void membersInjectPrimitive() {
60     Source component =
61         CompilerTests.javaSource(
62             "test.TestComponent",
63             "package test;",
64             "",
65             "import dagger.Component;",
66             "",
67             "@Component",
68             "interface TestComponent {",
69             "  void inject(int primitive);",
70             "}");
71     CompilerTests.daggerCompiler(component)
72         .compile(
73             subject -> {
74               subject.hasErrorCount(1);
75               subject.hasErrorContaining("Cannot inject members into int")
76                   .onSource(component)
77                   .onLineContaining("void inject(int primitive);");
78             });
79   }
80 
81   @Test
membersInjectArray()82   public void membersInjectArray() {
83     Source component =
84         CompilerTests.javaSource(
85             "test.TestComponent",
86             "package test;",
87             "",
88             "import dagger.Component;",
89             "",
90             "@Component",
91             "interface TestComponent {",
92             "  void inject(Object[] array);",
93             "}");
94     CompilerTests.daggerCompiler(component)
95         .compile(
96             subject -> {
97               subject.hasErrorCount(1);
98               subject.hasErrorContaining("Cannot inject members into java.lang.Object[]")
99                   .onSource(component)
100                   .onLineContaining("void inject(Object[] array);");
101             });
102   }
103 
104   @Test
membersInjectorOfArray()105   public void membersInjectorOfArray() {
106     Source component =
107         CompilerTests.javaSource(
108             "test.TestComponent",
109             "package test;",
110             "",
111             "import dagger.Component;",
112             "import dagger.MembersInjector;",
113             "",
114             "@Component",
115             "interface TestComponent {",
116             "  MembersInjector<Object[]> objectArrayInjector();",
117             "}");
118     CompilerTests.daggerCompiler(component)
119         .compile(
120             subject -> {
121               subject.hasErrorCount(1);
122               subject.hasErrorContaining("Cannot inject members into java.lang.Object[]")
123                   .onSource(component)
124                   .onLineContaining("objectArrayInjector();");
125             });
126   }
127 
128   @Test
membersInjectRawType()129   public void membersInjectRawType() {
130     Source component =
131         CompilerTests.javaSource(
132             "test.TestComponent",
133             "package test;",
134             "",
135             "import dagger.Component;",
136             "import java.util.Set;",
137             "",
138             "@Component",
139             "interface TestComponent {",
140             "  void inject(Set rawSet);",
141             "}");
142     CompilerTests.daggerCompiler(component)
143         .compile(
144             subject -> {
145               subject.hasErrorCount(1);
146               subject.hasErrorContaining("Cannot inject members into raw type java.util.Set");
147             });
148   }
149 
150   @Test
qualifiedMembersInjector()151   public void qualifiedMembersInjector() {
152     Source component =
153         CompilerTests.javaSource(
154             "test.TestComponent",
155             "package test;",
156             "",
157             "import dagger.Component;",
158             "import dagger.MembersInjector;",
159             "import javax.inject.Named;",
160             "",
161             "@Component",
162             "interface TestComponent {",
163             "  @Named(\"foo\") MembersInjector<Object> objectInjector();",
164             "}");
165     CompilerTests.daggerCompiler(component)
166         .compile(
167             subject -> {
168               subject.hasErrorCount(1);
169               subject.hasErrorContaining("Cannot inject members into qualified types")
170                   .onSource(component)
171                   .onLineContaining("objectInjector();");
172             });
173   }
174 
175   @Test
qualifiedMembersInjectionMethod()176   public void qualifiedMembersInjectionMethod() {
177     Source component =
178         CompilerTests.javaSource(
179             "test.TestComponent",
180             "package test;",
181             "",
182             "import dagger.Component;",
183             "import dagger.MembersInjector;",
184             "import javax.inject.Named;",
185             "",
186             "@Component",
187             "interface TestComponent {",
188             "  @Named(\"foo\") void injectObject(Object object);",
189             "}");
190     CompilerTests.daggerCompiler(component)
191         .compile(
192             subject -> {
193               subject.hasErrorCount(1);
194               subject.hasErrorContaining("Cannot inject members into qualified types")
195                   .onSource(component)
196                   .onLineContaining("injectObject(Object object);");
197             });
198   }
199 
200   @Test
qualifiedMembersInjectionMethodParameter()201   public void qualifiedMembersInjectionMethodParameter() {
202     Source component =
203         CompilerTests.javaSource(
204             "test.TestComponent",
205             "package test;",
206             "",
207             "import dagger.Component;",
208             "import dagger.MembersInjector;",
209             "import javax.inject.Named;",
210             "",
211             "@Component",
212             "interface TestComponent {",
213             "  void injectObject(@Named(\"foo\") Object object);",
214             "}");
215     CompilerTests.daggerCompiler(component)
216         .compile(
217             subject -> {
218               subject.hasErrorCount(1);
219               subject.hasErrorContaining("Cannot inject members into qualified types")
220                   .onSource(component)
221                   .onLineContaining("injectObject(@Named(\"foo\") Object object);");
222             });
223   }
224 
225   @Test
staticFieldInjection()226   public void staticFieldInjection() {
227     Source injected =
228         CompilerTests.javaSource(
229             "test.Injected",
230             "package test;",
231             "",
232             "import javax.inject.Inject;",
233             "",
234             "final class Injected {",
235             "  @Inject static Object object;",
236             "}");
237     Source component =
238         CompilerTests.javaSource(
239             "test.TestComponent",
240             "package test;",
241             "",
242             "import dagger.Component;",
243             "",
244             "@Component",
245             "interface TestComponent {",
246             "  void inject(Injected injected);",
247             "}");
248     CompilerTests.daggerCompiler(injected, component)
249         .compile(
250             subject -> {
251               subject.hasErrorCount(2);
252               subject.hasErrorContaining("static fields")
253                   .onSource(injected)
254                   .onLine(6);
255               subject.hasErrorContaining(
256                   "Injected cannot be provided without an @Inject constructor or an "
257                       + "@Provides-annotated method.");
258             });
259   }
260 
261   @Test
missingMembersInjectorForKotlinProperty()262   public void missingMembersInjectorForKotlinProperty() {
263     Source component =
264         CompilerTests.javaSource(
265             "test.TestComponent",
266             "package test;",
267             "",
268             "import dagger.Component;",
269             "import dagger.internal.codegen.KotlinInjectedQualifier;",
270             "",
271             "@Component(modules = TestModule.class)",
272             "interface TestComponent {",
273             "  void inject(KotlinInjectedQualifier injected);",
274             "}");
275     Source module =
276         CompilerTests.javaSource(
277             "test.TestModule",
278             "package test;",
279             "",
280             "import dagger.Module;",
281             "import dagger.Provides;",
282             "import javax.inject.Named;",
283             "",
284             "@Module",
285             "class TestModule {",
286             "  @Provides",
287             "  @Named(\"TheString\")",
288             "  String theString() { return \"\"; }",
289             "}");
290     CompilerTests.daggerCompiler(component, module)
291         .compile(
292             subject -> {
293               switch (CompilerTests.backend(subject)) {
294                 case KSP:
295                   // KSP works fine in this case so we shouldn't expect any errors here.
296                   subject.hasErrorCount(0);
297                   break;
298                 case JAVAC:
299                   subject.hasErrorCount(2);
300                   subject.hasErrorContaining(
301                       "Unable to read annotations on an injected Kotlin property.");
302                   subject.hasErrorContaining("KotlinInjectedQualifier cannot be provided");
303                   break;
304               }
305             });
306   }
307 
308   @Test
memberInjectionForKotlinObjectFails()309   public void memberInjectionForKotlinObjectFails() {
310     Source component =
311         CompilerTests.javaSource(
312             "test.TestComponent",
313             "package test;",
314             "",
315             "import dagger.Component;",
316             "import dagger.internal.codegen.KotlinObjectWithMemberInjection;",
317             "",
318             "@Component(modules = TestModule.class)",
319             "interface TestComponent {",
320             "  void inject(KotlinObjectWithMemberInjection injected);",
321             "}");
322     CompilerTests.daggerCompiler(component, testModule)
323         .compile(
324             subject -> {
325               switch (CompilerTests.backend(subject)) {
326                 case KSP:
327                   subject.hasErrorCount(2);
328                   break;
329                 case JAVAC:
330                   subject.hasErrorCount(3);
331                   subject.hasErrorContaining(
332                       "Dagger does not support injection into static fields");
333                   break;
334               }
335               subject.hasErrorContaining("Dagger does not support injection into Kotlin objects");
336               subject.hasErrorContaining("KotlinObjectWithMemberInjection cannot be provided");
337             });
338   }
339 
340   @Test
setterMemberInjectionForKotlinObjectFails()341   public void setterMemberInjectionForKotlinObjectFails() {
342     Source component =
343         CompilerTests.javaSource(
344             "test.TestComponent",
345             "package test;",
346             "",
347             "import dagger.Component;",
348             "import dagger.internal.codegen.KotlinObjectWithSetterMemberInjection;",
349             "",
350             "@Component(modules = TestModule.class)",
351             "interface TestComponent {",
352             "  void inject(KotlinObjectWithSetterMemberInjection injected);",
353             "}");
354     CompilerTests.daggerCompiler(component, testModule)
355         .compile(
356             subject -> {
357               subject.hasErrorCount(2);
358               subject.hasErrorContaining("Dagger does not support injection into Kotlin objects");
359               subject.hasErrorContaining(
360                   "KotlinObjectWithSetterMemberInjection cannot be provided without an "
361                       + "@Provides-annotated method.");
362             });
363   }
364 
365   @Test
memberInjectionForKotlinClassWithCompanionObjectFails()366   public void memberInjectionForKotlinClassWithCompanionObjectFails() {
367     Source component =
368         CompilerTests.javaSource(
369             "test.TestComponent",
370             "package test;",
371             "",
372             "import dagger.Component;",
373             "import dagger.internal.codegen.KotlinClassWithMemberInjectedCompanion;",
374             "",
375             "@Component(modules = TestModule.class)",
376             "interface TestComponent {",
377             "  void inject(KotlinClassWithMemberInjectedCompanion injected);",
378             "  void injectCompanion(KotlinClassWithMemberInjectedCompanion.Companion injected);",
379             "}");
380     CompilerTests.daggerCompiler(component, testModule)
381         .compile(
382             subject -> {
383               switch (CompilerTests.backend(subject)) {
384                 case KSP:
385                   subject.hasErrorCount(4);
386                   subject.hasErrorContaining(
387                       "Dagger does not support injection into Kotlin objects");
388                   break;
389                 case JAVAC:
390                   subject.hasErrorCount(2);
391                   break;
392               }
393               subject.hasErrorContaining("Dagger does not support injection into static fields");
394               subject.hasErrorContaining(
395                   "KotlinClassWithMemberInjectedCompanion cannot be provided");
396             });
397   }
398 
399   @Test
setterMemberInjectionForKotlinClassWithCompanionObjectFails()400   public void setterMemberInjectionForKotlinClassWithCompanionObjectFails() {
401     Source component =
402         CompilerTests.javaSource(
403             "test.TestComponent",
404             "package test;",
405             "",
406             "import dagger.Component;",
407             "import dagger.internal.codegen.KotlinClassWithSetterMemberInjectedCompanion;",
408             "",
409             "@Component(modules = TestModule.class)",
410             "interface TestComponent {",
411             "  void inject(KotlinClassWithSetterMemberInjectedCompanion.Companion injected);",
412             "}");
413     CompilerTests.daggerCompiler(component, testModule)
414         .compile(
415             subject -> {
416               switch (CompilerTests.backend(subject)) {
417                 case KSP:
418                   // TODO(b/268257007): The KSP results should match KAPT once this bug is fixed.
419                   subject.hasErrorCount(3);
420                   subject.hasErrorContaining(
421                       "Dagger does not support injection into static methods");
422                   break;
423                 case JAVAC:
424                   subject.hasErrorCount(2);
425                   break;
426               }
427               subject.hasErrorContaining("Dagger does not support injection into Kotlin objects");
428               subject.hasErrorContaining(
429                   "KotlinClassWithSetterMemberInjectedCompanion.Companion cannot be provided");
430             });
431   }
432 
433   @Test
memberInjectionForKotlinClassWithNamedCompanionObjectFails()434   public void memberInjectionForKotlinClassWithNamedCompanionObjectFails() {
435     Source component =
436         CompilerTests.javaSource(
437             "test.TestComponent",
438             "package test;",
439             "",
440             "import dagger.Component;",
441             "import dagger.internal.codegen.KotlinClassWithMemberInjectedNamedCompanion;",
442             "",
443             "@Component(modules = TestModule.class)",
444             "interface TestComponent {",
445             "  void inject(KotlinClassWithMemberInjectedNamedCompanion injected);",
446             "  void injectCompanion(KotlinClassWithMemberInjectedNamedCompanion.TheCompanion"
447                 + " injected);",
448             "}");
449     CompilerTests.daggerCompiler(component, testModule)
450         .compile(
451             subject -> {
452               switch (CompilerTests.backend(subject)) {
453                 case KSP:
454                   subject.hasErrorCount(4);
455                   subject.hasErrorContaining(
456                       "Dagger does not support injection into Kotlin objects");
457                   break;
458                 case JAVAC:
459                   subject.hasErrorCount(2);
460                   break;
461               }
462               subject.hasErrorContaining("Dagger does not support injection into static fields");
463               subject.hasErrorContaining(
464                   "KotlinClassWithMemberInjectedNamedCompanion cannot be provided");
465             });
466   }
467 
468   @Test
setterMemberInjectionForKotlinClassWithNamedCompanionObjectFails()469   public void setterMemberInjectionForKotlinClassWithNamedCompanionObjectFails() {
470     Source component =
471         CompilerTests.javaSource(
472             "test.TestComponent",
473             "package test;",
474             "",
475             "import dagger.Component;",
476             "import dagger.internal.codegen.KotlinClassWithSetterMemberInjectedNamedCompanion;",
477             "",
478             "@Component(modules = TestModule.class)",
479             "interface TestComponent {",
480             "  void inject(",
481             "      KotlinClassWithSetterMemberInjectedNamedCompanion.TheCompanion injected);",
482             "}");
483     CompilerTests.daggerCompiler(component, testModule)
484         .compile(
485             subject -> {
486               switch (CompilerTests.backend(subject)) {
487                 case KSP:
488                   // TODO(b/268257007): The KSP results should match KAPT once this bug is fixed.
489                   subject.hasErrorCount(3);
490                   subject.hasErrorContaining(
491                       "Dagger does not support injection into static methods");
492                   break;
493                 case JAVAC:
494                   subject.hasErrorCount(2);
495                   break;
496               }
497               subject.hasErrorContaining("Dagger does not support injection into Kotlin objects");
498               subject.hasErrorContaining(
499                   "KotlinClassWithSetterMemberInjectedNamedCompanion.TheCompanion "
500                       + "cannot be provided");
501             });
502   }
503 
504   private final Source testModule =
505         CompilerTests.javaSource(
506           "test.TestModule",
507           "package test;",
508           "",
509           "import dagger.Module;",
510           "import dagger.Provides;",
511           "",
512           "@Module",
513           "class TestModule {",
514           "  @Provides",
515           "  String theString() { return \"\"; }",
516           "}");
517 }
518