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